247

Can I define custom types for user-defined exceptions in JavaScript? If so, how would I do it?

2
  • 3
    Beware. According to JavaScript in 10 Minutes you won't get a stack trace if you throw an unboxed value. Commented Dec 20, 2011 at 3:13
  • exceptionsjs.com provides the ability to create custom exceptions and provides some missing exceptions including ArgumentException and NotImplemented by default. Commented Aug 4, 2014 at 2:37

13 Answers 13

236

From WebReference:

throw { 
  name:        "System Error", 
  level:       "Show Stopper", 
  message:     "Error detected. Please contact the system administrator.", 
  htmlMessage: "Error detected. Please contact the <a href=\"mailto:[email protected]\">system administrator</a>.",
  toString:    function(){return this.name + ": " + this.message;} 
}; 
8
  • 7
    @b.long It's in "JavaScript: The Good Parts" (great book IMO). This Google Books preview shows the section: books.google.com/books?id=PXa2bby0oQ0C&pg=PA32&lpg=PA32
    – orip
    Commented Feb 14, 2013 at 8:24
  • 12
    Adding a toString method will make it show nicely in the javascript console. without it shows like: Uncaught #<Object> with the toString it shows like: Uncaught System Error: Error detected. Please contact the system administrator.
    – JDC
    Commented Aug 16, 2013 at 7:06
  • 14
    This will not allow you stack traces unless you inherit from Error
    – Luke H
    Commented Jul 30, 2014 at 19:53
  • 1
    How can you filter within a catch block to only work with this custom error ?
    – Overdrivr
    Commented Mar 24, 2020 at 7:57
  • 3
    This does not explain how to create a user-defined exception. -1 because there is only code and no explanation. Are these fields mandatory? Are these fields optional? Does the toString() function have a specific meaning for some browsers or some other JavaScript functions? etc. Commented Jun 23, 2021 at 19:46
121

You should create a custom exception that extends Error:

class InvalidArgumentException extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
  }
}

Note: Setting this.name is required for .toString() and .stack to print InvalidArgumentException instead of Error.

For older Javascript environments, you can do prototypical inheritance:

function InvalidArgumentException(message) {
    this.message = message;
    // Use V8's native method if available, otherwise fallback
    if ("captureStackTrace" in Error)
        Error.captureStackTrace(this, InvalidArgumentException);
    else
        this.stack = (new Error()).stack;
}

InvalidArgumentException.prototype = Object.create(Error.prototype);
InvalidArgumentException.prototype.name = "InvalidArgumentException";
InvalidArgumentException.prototype.constructor = InvalidArgumentException;

This is basically a simplified version of what disfated posted with the enhancement that stack traces work on Firefox and other browsers.

Both satisfy all of these tests:

Usage:

throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");

And it will behave is expected:

err instanceof InvalidArgumentException          // -> true
err instanceof Error                             // -> true
InvalidArgumentException.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err)               // -> true
err.constructor.name                             // -> InvalidArgumentException
err.name                                         // -> InvalidArgumentException
err.message                                      // -> Not yet...
err.toString()                                   // -> InvalidArgumentException: Not yet...
err.stack                                        // -> works fine!
0
83

You could implement your own exceptions and their handling for example like here:

// define exceptions "classes" 
function NotNumberException() {}
function NotPositiveNumberException() {}

// try some code
try {
    // some function/code that can throw
    if (isNaN(value))
        throw new NotNumberException();
    else
    if (value < 0)
        throw new NotPositiveNumberException();
}
catch (e) {
    if (e instanceof NotNumberException) {
        alert("not a number");
    }
    else
    if (e instanceof NotPositiveNumberException) {
        alert("not a positive number");
    }
}

There is another syntax for catching a typed exception, although this won't work in every browser (for example not in IE):

// define exceptions "classes" 
function NotNumberException() {}
function NotPositiveNumberException() {}

// try some code
try {
    // some function/code that can throw
    if (isNaN(value))
        throw new NotNumberException();
    else
    if (value < 0)
        throw new NotPositiveNumberException();
}
catch (e if e instanceof NotNumberException) {
    alert("not a number");
}
catch (e if e instanceof NotPositiveNumberException) {
    alert("not a positive number");
}
1
  • 3
    The MSN website carries this warning about condition catches: Non-standard This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future. Commented Feb 18, 2014 at 22:51
41

Yes. You can throw anything you want: integers, strings, objects, whatever. If you want to throw an object, then simply create a new object, just as you would create one under other circumstances, and then throw it. Mozilla's Javascript reference has several examples.

0
27
function MyError(message) {
 this.message = message;
}

MyError.prototype = new Error;

This allows for usage like..

try {
  something();
 } catch(e) {
  if(e instanceof MyError)
   doSomethingElse();
  else if(e instanceof Error)
   andNowForSomethingCompletelyDifferent();
}
4
  • Wouldn't this brief example work exactly the same way even if you didn't inherit Error's prototype? It's not clear to me what that gains you in this example. Commented Jul 27, 2013 at 18:50
  • 1
    No, e instanceof Error would be false. Commented Jul 30, 2013 at 17:43
  • Indeed. But since e instanceof MyError would be true, the else if(e instanceof Error) statement would never be evaluated. Commented Jul 31, 2013 at 2:13
  • 1
    Right, this is just an example of how this style of try/catch would work. Where else if(e instanceof Error) would be the last catch. Likely followed by a simple else (which I did not include). Sort of like the default: in a switch statement but for errors. Commented Jul 31, 2013 at 4:47
23

In short:

Option 1: use babel-plugin-transform-builtin-extend

Option 2: do it yourself (inspired from that same library)

    function CustomError(...args) {
      const instance = Reflect.construct(Error, args);
      Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
      return instance;
    }
    CustomError.prototype = Object.create(Error.prototype, {
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    Reflect.setPrototypeOf(CustomError, Error);
  • If you are using pure ES5:

    function CustomError(message, fileName, lineNumber) {
      const instance = new Error(message, fileName, lineNumber);
      Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
      return instance;
    }
    CustomError.prototype = Object.create(Error.prototype, {
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    if (Object.setPrototypeOf){
        Object.setPrototypeOf(CustomError, Error);
    } else {
        CustomError.__proto__ = Error;
    }
    
  • Alternative: use Classtrophobic framework

Explanation:

Why extending the Error class using ES6 and Babel is a problem?

Because an instance of CustomError is not anymore recognized as such.

class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false

In fact, from the official documentation of Babel, you cannot extend any built-in JavaScript classes such as Date, Array, DOM or Error.

The issue is described here:

What about the other SO answers?

All the given answers fix the instanceof issue but you lose the regular error console.log:

console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}

Whereas using the method mentioned above, not only you fix the instanceof issue but you also keep the regular error console.log:

console.log(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5
13

ES6

With the new class and extend keywords it’s now much easier:

class CustomError extends Error {
  constructor(message) {
    super(message);
    //something
  }
}
11

Here is how you can create custom errors with completely identical to native Error's behaviour. This technique works only in Chrome and node.js for now. I also wouldn't recommend to use it if you don't understand what it does.

Error.createCustromConstructor = (function() {

    function define(obj, prop, value) {
        Object.defineProperty(obj, prop, {
            value: value,
            configurable: true,
            enumerable: false,
            writable: true
        });
    }

    return function(name, init, proto) {
        var CustomError;
        proto = proto || {};
        function build(message) {
            var self = this instanceof CustomError
                ? this
                : Object.create(CustomError.prototype);
            Error.apply(self, arguments);
            Error.captureStackTrace(self, CustomError);
            if (message != undefined) {
                define(self, 'message', String(message));
            }
            define(self, 'arguments', undefined);
            define(self, 'type', undefined);
            if (typeof init == 'function') {
                init.apply(self, arguments);
            }
            return self;
        }
        eval('CustomError = function ' + name + '() {' +
            'return build.apply(this, arguments); }');
        CustomError.prototype = Object.create(Error.prototype);
        define(CustomError.prototype, 'constructor', CustomError);
        for (var key in proto) {
            define(CustomError.prototype, key, proto[key]);
        }
        Object.defineProperty(CustomError.prototype, 'name', { value: name });
        return CustomError;
    }

})();

As a reasult we get

/**
 * name   The name of the constructor name
 * init   User-defined initialization function
 * proto  It's enumerable members will be added to 
 *        prototype of created constructor
 **/
Error.createCustromConstructor = function(name, init, proto)

Then you can use it like this:

var NotImplementedError = Error.createCustromConstructor('NotImplementedError');

And use NotImplementedError as you would Error:

throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');

And it will behave is expected:

err instanceof NotImplementedError               // -> true
err instanceof Error                             // -> true
NotImplementedError.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err)               // -> true
err.constructor.name                             // -> NotImplementedError
err.name                                         // -> NotImplementedError
err.message                                      // -> Not yet...
err.toString()                                   // -> NotImplementedError: Not yet...
err.stack                                        // -> works fine!

Note, that error.stack works absolutle correct and won't include NotImplementedError constructor call (thanks to v8's Error.captureStackTrace()).

Note. There is ugly eval(). The only reason it is used is to get correct err.constructor.name. If you don't need it, you can a bit simplify everything.

1
11

I often use an approach with prototypal inheritance. Overriding toString() gives you the advantage that tools like Firebug will log the actual information instead of [object Object] to the console for uncaught exceptions.

Use instanceof to determine the type of exception.

main.js

// just an exemplary namespace
var ns = ns || {};

// include JavaScript of the following
// source files here (e.g. by concatenation)

var someId = 42;
throw new ns.DuplicateIdException('Another item with ID ' +
    someId + ' has been created');
// Firebug console:
// uncaught exception: [Duplicate ID] Another item with ID 42 has been created

Exception.js

ns.Exception = function() {
}

/**
 * Form a string of relevant information.
 *
 * When providing this method, tools like Firebug show the returned 
 * string instead of [object Object] for uncaught exceptions.
 *
 * @return {String} information about the exception
 */
ns.Exception.prototype.toString = function() {
    var name = this.name || 'unknown';
    var message = this.message || 'no description';
    return '[' + name + '] ' + message;
};

DuplicateIdException.js

ns.DuplicateIdException = function(message) {
    this.name = 'Duplicate ID';
    this.message = message;
};

ns.DuplicateIdException.prototype = new ns.Exception();
7

Use the throw statement.

JavaScript doesn't care what the exception type is (as Java does). JavaScript just notices, there's an exception and when you catch it, you can "look" what the exception "says".

If you have different exception types you have to throw, I'd suggest to use variables which contain the string/object of the exception i.e. message. Where you need it use "throw myException" and in the catch, compare the caught exception to myException.

1

See this example in the MDN.

If you need to define multiple Errors (test the code here!):

function createErrorType(name, initFunction) {
    function E(message) {
        this.message = message;
        if (Error.captureStackTrace)
            Error.captureStackTrace(this, this.constructor);
        else
            this.stack = (new Error()).stack;
        initFunction && initFunction.apply(this, arguments);
    }
    E.prototype = Object.create(Error.prototype);
    E.prototype.name = name;
    E.prototype.constructor = E;
    return E;
}
var InvalidStateError = createErrorType(
    'InvalidStateError',
    function (invalidState, acceptedStates) {
        this.message = 'The state ' + invalidState + ' is invalid. Expected ' + acceptedStates + '.';
    });

var error = new InvalidStateError('foo', 'bar or baz');
function assert(condition) { if (!condition) throw new Error(); }
assert(error.message);
assert(error instanceof InvalidStateError);  
assert(error instanceof Error); 
assert(error.name == 'InvalidStateError');
assert(error.stack);
error.message;

Code is mostly copied from: What's a good way to extend Error in JavaScript?

1

An alternative to the answer of asselin for use with ES2015 classes

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}
1
//create error object
var error = new Object();
error.reason="some reason!";

//business function
function exception(){
    try{
        throw error;
    }catch(err){
        err.reason;
    }
}

Now we set add the reason or whatever properties we want to the error object and retrieve it. By making the error more reasonable.

Not the answer you're looking for? Browse other questions tagged or ask your own question.