540

I want to throw some things in my JS code and I want them to be instanceof Error, but I also want to have them be something else.

In Python, typically, one would subclass Exception.

What's the appropriate thing to do in JS?

askedSep 5, 2009 at 0:54
Josh Gibson's user avatar
0

27 Answers27

335

In ES6:

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

source

Kostanos's user avatar
Kostanos
10.6k4 gold badges60 silver badges70 bronze badges
answeredSep 23, 2015 at 22:51
Mohsen's user avatar
Sign up to request clarification or add additional context in comments.

12 Comments

Worth mentioning that this doesn't work if you are using ES6 features via a transpiler, such as Babel, since subclasses must extend a class.
If you are using babel and are on node > 5.x you shouldn't be using the es2015 preset butnpmjs.com/package/babel-preset-node5 would allow you to use native es6 extends plus more
For ease of maintaining, usethis.name = this.constructor.name; instead.
This already works in the latest babel preset, they fixed the instanceof issue, also minified bugs are probably wrong minifier configs
@КонстантинВан That's unfortunately only an option without class-name minification.
|
263

The only standard field Error object has is themessage property. (SeeMDN, or EcmaScript Language Specification, section 15.11)Everything else is platform specific.

Mosts environments set thestack property, butfileName andlineNumber are practically useless to be used in inheritance.

So, the minimalistic approach is:

function MyError(message) {    this.name = 'MyError';    this.message = message;    this.stack = (new Error()).stack;}MyError.prototype = new Error;  // <-- remove this if you do not                                 //     want MyError to be instanceof Error

You could sniff the stack, unshift unwanted elements from it and extract information like fileName and lineNumber, but doing so requires information about the platform JavaScript is currently running upon. Most cases that is unnecessary -- and you can do it in post-mortem if you really want.

Safari is a notable exception. There is nostack property, but thethrow keyword setssourceURL andline properties of the object that is being thrown. Those things are guaranteed to be correct.

Test cases I used can be found here:JavaScript self-made Error object comparison.

robocat's user avatar
robocat
5,45250 silver badges66 bronze badges
answeredMar 9, 2011 at 20:05
Tero's user avatar

11 Comments

You could move thethis.name = 'MyError' outside of the function and change it toMyError.prototype.name = 'MyError'.
This is the only correct answer here, although as a matter of style, I'd probably write it like this.function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
I'd addMyError.prototype.constructor = MyError too.
MyError.prototype = Object.create(Error.prototype);
@Silverspur The act of assigningMyError.prototype = Object.create(Error.prototype) (or new Error) is overwriting all members ofMyError.prototype. ThereforeMyError.prototype.constructor is actuallyError.prototype.constructor, since we just assigned the entireError.prototype toMyError.prototype. This is a side effect of JavaScript not really an object-oriented language, but a prototypal language. So this is why we assignMyError.prototype.constructor = MyError back to the MyError constructor function.
|
88

In short:

  • If you are using ES6without transpilers:

    class CustomError extends Error { /* ... */}
  • If you are usingBabel transpiler:

Option 1: usebabel-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 usingpure ES5:

    function CustomError(message, fileName, lineNumber) {  var 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: useClasstrophobic 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);// trueconsole.log(new CustomError('test') instanceof CustomError);// false

In fact, from the official documentation of Babel, youcannot extend any built-in JavaScript classes such asDate,Array,DOM orError.

The issue is described here:

What about the other SO answers?

All the given answers fix theinstanceof issue but you lose the regular errorconsole.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 theinstanceof issue but you also keep the regular errorconsole.log:

console.log(new CustomError('test'));// output:// Error: test//     at CustomError (<anonymous>:2:32)//     at <anonymous>:1:5
Indolering's user avatar
Indolering
3,13433 silver badges46 bronze badges
answeredApr 24, 2017 at 18:11
JBE's user avatar

5 Comments

class CustomError extends Error { /* ... */} doesn't correctly handle vendor-specific arguments (lineNumber, etc), 'Extending Error in Javascript with ES6 syntax' is Babel specific, your ES5 solution usesconst and it doesn't handle custom arguments.
Very complete answer.
This actually provides the most comprehensive solution and explains why the various pieces are necessary. Thanks so much JBE!
This helped me in solving the problem with inheriting from "Error". It was a two hours nightmare!
Worth noting that the issue withconsole.log(new CustomError('test') instanceof CustomError);// false was true at the time of writing but has now been resolved. In factthe issue linked in the answer has been resolved and we can test the correct behaviourhere and by pasting the code in theREPL and seeing how it gets correctly transpiled to instantiate with the correct prototype chain.
50

Edit: Please read comments. It turns out this only works well in V8 (Chrome / Node.JS) My intent was to provide a cross-browser solution, which would work in all browsers, and provide stack trace where support is there.

Edit: I made this Community Wiki to allow for more editing.

Solution for V8 (Chrome / Node.JS), works in Firefox, and can be modified to function mostly correctly in IE. (see end of post)

function UserError(message) {  this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.  Error.call(this) // Does not seem necessary. Perhaps remove this line?  Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter  this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"  this.message = message; // Used to set the message}

Original post on "Show me the code !"

Short version:

function UserError(message) {  this.constructor.prototype.__proto__ = Error.prototype  Error.captureStackTrace(this, this.constructor)  this.name = this.constructor.name  this.message = message}

I keepthis.constructor.prototype.__proto__ = Error.prototype inside the function to keep all the code together. But you can also replacethis.constructor withUserError and that allows you to move the code to outside the function, so it only gets called once.

If you go that route, make sure you call that linebefore the first time you throwUserError.

That caveat does not apply the function, because functions are created first, no matter the order. Thus, you can move the function to the end of the file, without a problem.

Browser Compatibility

Works in Firefox and Chrome (and Node.JS) and fills all promises.

Internet Explorer fails in the following

  • Errors do not haveerr.stack to begin with, so "it's not my fault".

  • Error.captureStackTrace(this, this.constructor) does not exist so you need to do something else like

    if(Error.captureStackTrace) // AKA if not IE    Error.captureStackTrace(this, this.constructor)
  • toString ceases to exist when you subclassError. So you also need to add.

    else    this.toString = function () { return this.name + ': ' + this.message }
  • IE will not considerUserError to be aninstanceof Error unless you run the following some time before youthrow UserError

    UserError.prototype = Error.prototype

11 Comments

I don't think Firefox actually has captureStackTrace. It's a V8 extension and is undefined in Firefox for me, nor can I find any references on the web to Firefox supporting it. (Thanks though!)
Error.call(this) is indeed not doing anything since itreturns an error rather than modifyingthis.
UserError.prototype = Error.prototype is misleading. This doesn't do inheritance, this makes themthe same class.
I believeObject.setPrototypeOf(this.constructor.prototype, Error.prototype) is preferred tothis.constructor.prototype.__proto__ = Error.prototype, at least for current browsers.
Why notthis.constructor.prototype = Object.create(Error.prototype);? Seems cleaner than using__proto__
|
31

Toavoid the boilerplate for every different type of error, I combined the wisdom of some of the solutions into a createErrorType function:

function createErrorType(name, init) {  function E(message) {    if (!Error.captureStackTrace)      this.stack = (new Error()).stack;    else      Error.captureStackTrace(this, this.constructor);    this.message = message;    init && init.apply(this, arguments);  }  E.prototype = new Error();  E.prototype.name = name;  E.prototype.constructor = E;  return E;}

Then you candefine new error types easily as follows:

var NameError = createErrorType('NameError', function (name, invalidChar) {  this.message = 'The name ' + name + ' may not contain ' + invalidChar;});var UnboundError = createErrorType('UnboundError', function (variableName) {  this.message = 'Variable ' + variableName + ' is not bound';});
answeredJan 13, 2015 at 15:31
Ruben Verborgh's user avatar

2 Comments

Is there a reason you still need the linethis.name = name;?
@PeterTseng Sincename is already set on the prototype, it's not necessary anymore. I removed it. Thanks!
29

In2018, I think this is the best way; that supports IE9+ and modern browsers.

UPDATE: Seethis test andrepo for comparison on different implementations.

function CustomError(message) {    Object.defineProperty(this, 'name', {        enumerable: false,        writable: false,        value: 'CustomError'    });    Object.defineProperty(this, 'message', {        enumerable: false,        writable: true,        value: message    });    if (Error.hasOwnProperty('captureStackTrace')) { // V8        Error.captureStackTrace(this, CustomError);    } else {        Object.defineProperty(this, 'stack', {            enumerable: false,            writable: false,            value: (new Error(message)).stack        });    }}if (typeof Object.setPrototypeOf === 'function') {    Object.setPrototypeOf(CustomError.prototype, Error.prototype);} else {    CustomError.prototype = Object.create(Error.prototype, {        constructor: { value: CustomError }    });}

Also beware that__proto__ property isdeprecated which is widely used in other answers.

answeredMar 9, 2016 at 1:52
Onur Yıldırım's user avatar

7 Comments

Why are you usingsetPrototypeOf()? At least according to MDN, it's generally discouraged to use it if you can accomplish the same thing by just setting the.prototype property on the constructor (as you're doing in theelse block for browses that don't havesetPrototypeOf).
Changing the prototype of an object is discouraged all together, notsetPrototypeOf. But if you still need it (as OP asks), you should use the built-in methodology. As MDN indicates, this is considered the proper way to set the prototype of an object. In other words, MDN says do not change the prototype (as it affects performance and optimization) but if you have to, usesetPrototypeOf.
My point was that I don't think you actually need to change the prototype here. You could simply use your line at the bottom (CustomError.prototype = Object.create(Error.prototype)). Also,Object.setPrototypeOf(CustomError, Error.prototype) is setting the prototype of the constructor itself rather than specifying the prototype for new instances ofCustomError. Anyway, in 2016 I think there's actually a better way to extend errors, although I'm still figuring out how to use it together with Babel:github.com/loganfsmyth/babel-plugin-transform-builtin-extend/…
CustomError.prototype = Object.create(Error.prototype) is also changing the prototype. You have to change it since there is no built-in extend/inherit logic in ES5. I'm sure the babel plugin you mention does similar things.
I created a gist demonstrating why usingObject.setPrototypeOf doesn't make sense here, at least not in the way you're using it:gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348. Perhaps you meant to writeObject.setPrototypeOf(CustomError.prototype, Error.prototype) - that would make slightly more sense (although still providing no benefit over simply settingCustomError.prototype).
|
23

As some people have said, it's fairly easy with ES6:

class CustomError extends Error { }

So I tried that within my app, (Angular, Typescript) and it just didn't work. After some time I've found that the problem is coming from Typescript :O

Seehttps://github.com/Microsoft/TypeScript/issues/13965

It's very disturbing because if you do:

class CustomError extends Error {}​try {  throw new CustomError()} catch(e) {  if (e instanceof CustomError) {    console.log('Custom error');  } else {    console.log('Basic error');  }}

In node or directly into your browser it'll display:Custom error

Try to run that with Typescript in your project on on Typescript playground, it'll displayBasic error...

The solution is to do the following:

class CustomError extends Error {  // we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965  // otherwise we cannot use instanceof later to catch a given type  public __proto__: Error;  constructor(message?: string) {    const trueProto = new.target.prototype;    super(message);    this.__proto__ = trueProto;  }}
answeredFeb 1, 2018 at 16:34
maxime1992's user avatar

5 Comments

Does that mean that whether this workaround is needed In a NodeJS context depends on thetarget setting intsconfig.json? If the target ises5 it is needed because the transpilation would lose the correct proto information otherwise. If the target ises6 and newer, the transpilation keeps theclass syntax, and NodeJS will handle it as expected without any workarounds?
Unsure, you'd have to test it ;)
Yes, I can only reproduce this issue when usingtarget: "es5".
Seems to be working as expected in TS 4.3.5:typescriptlang.org/play?#code/…
To clarify @maxime1992's day-saving comment,ES3 andES5 fail; anything fromES2015 on preserves the CustomError prototype as expected. Of course,CustomError is alsoinstanceof Error so tests should test for the most specific class.
23

IMPORTANT: this answer is from 2016 and is now potentially outdated. JavaScript in general and Node.js in particular advanced quite a lot and now offer more syntax possibilities to achieve the same results. The content below is kept for historical reasons and just in case someone is dealing with legacy Node.js versions out there somewhere.


Original answer:

For the sake of completeness -- just because none of the previous answers mentioned this method -- if you are working with Node.js and don't have to care about browser compatibility, the desired effect is pretty easy to achieve with the built ininherits of theutil module (official docs here).

For example, let's suppose you want to create a custom error class that takes an error code as the first argument and the error message as the second argument:

filecustom-error.js:

'use strict';var util = require('util');function CustomError(code, message) {  Error.captureStackTrace(this, CustomError);  this.name = CustomError.name;  this.code = code;  this.message = message;}util.inherits(CustomError, Error);module.exports = CustomError;

Now you can instantiate and pass/throw yourCustomError:

var CustomError = require('./path/to/custom-error');// pass as the first argument to your callbackcallback(new CustomError(404, 'Not found!'));// or, if you are working with try/catch, throw itthrow new CustomError(500, 'Server Error!');

Note that, with this snippet, the stack trace will have the correct file name and line, and the error instance will have the correct name!

This happens due to the usage of thecaptureStackTrace method, which creates astack property on the target object (in this case, theCustomError being instantiated). For more details about how it works, check the documentationhere.

answeredMar 13, 2016 at 4:37
Victor Schröder's user avatar

4 Comments

this.message = this.message; is this wrong or are there still crazy things I don't know about JS?
Hey @Alex, you are totally right! It's fixed now. Thanks!
utils.inherits is discouraged and legacy on node > 16.x.x. Node encourages to use class and extend insteadnodejs.org/dist/latest-v16.x/docs/api/…
Yes, @nicoramirezdev, but this answer dates back to 2016. I'll add a warning at the beginning, but keep the content for historical reasons.
21

I didn't like all the other answers, too long, too complicated or didn't trace the stack correctly. Here my approach, if you need more custom props pass them to the constructor and set them like name.

class CustomError extends Error {  constructor (message) {    super(message)    // needed for CustomError instanceof Error => true    Object.setPrototypeOf(this, new.target.prototype);    // Set the name    this.name = this.constructor.name    // Maintains proper stack trace for where our error was thrown (only available on V8)    if (Error.captureStackTrace) {      Error.captureStackTrace(this, this.constructor)    }  }}// create own CustomError sub classesclass SubCustomError extends CustomError{}// Testsconsole.log(new SubCustomError instanceof CustomError) // true console.log(new CustomError instanceof Error) // trueconsole.log(new SubCustomError instanceof Error) // truethrow new SubCustomError ('test error')
answeredApr 23, 2021 at 13:20
SePeF's user avatar

4 Comments

The only answer that works, in dept.
THANK you!Error.captureStackTrace(this, this.constructor)! Duh, now I look at it, but that was eluding me for hours. Good gods I feel like I've just spent 45 minutes reading a dozen+ pedantically- masturbatory code treatises before finally getting here. Well done! Also, thanks for chiming in years after the fact, too (this is not sarcasm; I really DO appreciate it, lol). Most people see an old question with a buncha answers and brush on past. Your 2 cents were a price I'd happily have paid at twice the cost.
IsObject.setPrototypeOf(this, new.target.prototype) really necessary? I was following along with Corman'sanswer to this Q, which doesn't have theObject.setProto... line, and my custom error is both an instance of itself (err1 instanceOf MyError) and its super (err1 instanceOf Error).
@ZachYoung OmittingsetPrototypeOf() works in JS running in the browser and in node, but not when using a transpiler like Babel. For an explanation see the "Explanation" part of this answerstackoverflow.com/a/43595019/9299798
20

Crescent Fresh's answer highly-voted answer is misleading. Though his warnings are invalid, there are other limitations that he doesn't address.

First, the reasoning in Crescent's "Caveats:" paragraph doesn't make sense. The explanation implies that coding "a bunch of if (error instanceof MyError) else ..." is somehow burdensome or verbose compared to multiple catch statements. Multiple instanceof statements in a single catch block are just as concise as multiple catch statements-- clean and concise code without any tricks. This is a great way to emulate Java's great throwable-subtype-specific error handling.

WRT "appears the message property of the subclass does not get set", that is not the case if you use a properly constructed Error subclass. To make your own ErrorX Error subclass, just copy the code block beginning with "var MyError =", changing the one word "MyError" to "ErrorX". (If you want to add custom methods to your subclass, follow the sample text).

The real and significant limitation of JavaScript error subclassing is that for JavaScript implementations or debuggers that track and report on stack trace and location-of-instantiation, like FireFox, a location in your own Error subclass implementation will be recorded as the instantiation point of the class, whereas if you used a direct Error, it would be the location where you ran "new Error(...)"). IE users would probably never notice, but users of Fire Bug on FF will see useless file name and line number values reported alongside these Errors, and will have to drill down on the stack trace to element #1 to find the real instantiation location.

answeredJul 23, 2010 at 14:20
Blaine's user avatar

3 Comments

Did I get it right - that if you do not subclass and use new Error(...) directly, then the file name and line is being reported properly? And you basically say that on practice (real and not just of sexy or decorative kinda) subclassing Errors, has no sense?
This answer is some what confusing asCrescent Fresh's has been deleted!
is this still the case?developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Line number is 2 not where new was called
16

On addition to standardmessage property, JavaScript now supports adding specificcause of the error as a optional param to theError constructor:

const error1 = new Error('Error one');const error2 = new Error('Error two', { cause: error1 });// error2.cause === error1
answeredSep 28, 2021 at 16:51
t_dom93's user avatar

2 Comments

In typescript new Error only accepts one parameter, do you know how to fix this ?
@DanielValencia It's supported as of TypeScript 4.6 when you target ES2022 (or later)
15

How about this solution?

Instead of throwing your custom Error using:

throw new MyError("Oops!");

You would wrap the Error object (kind of like a Decorator):

throw new MyError(Error("Oops!"));

This makes sure all of the attributes are correct, such as the stack, fileName lineNumber, et cetera.

All you have to do then is either copy over the attributes, or define getters for them.Here is an example using getters (IE9):

function MyError(wrapped){        this.wrapped = wrapped;        this.wrapped.name = 'MyError';}function wrap(attr){        Object.defineProperty(MyError.prototype, attr, {                get: function()                {                        return this.wrapped[attr];                }        });}MyError.prototype = Object.create(Error.prototype);MyError.prototype.constructor = MyError;wrap('name');wrap('message');wrap('stack');wrap('fileName');wrap('lineNumber');wrap('columnNumber');MyError.prototype.toString = function(){        return this.wrapped.toString();};
answeredAug 16, 2014 at 22:35
JoWie's user avatar

3 Comments

I've released this solution as a npm package:npmjs.com/package/throwable
Incredibly elegant solution, thanks for sharing! One variation :new MyErr (arg1, arg2, new Error()) and in MyErr constructor we useObject.assign to assign the last arg's properties tothis
I like this. You bypass a limitation by using encapsulation instead of inheritance.
13

My solution is more simple than the other answers provided and doesn't have the downsides.

It preserves the Error prototype chain and all properties on Error without needing specific knowledge of them. It's been tested in Chrome, Firefox, Node, and IE11.

The only limitation is an extra entry at the top of the call stack. But that is easily ignored.

Here's an example with two custom parameters:

function CustomError(message, param1, param2) {    var err = new Error(message);    Object.setPrototypeOf(err, CustomError.prototype);    err.param1 = param1;    err.param2 = param2;    return err;}CustomError.prototype = Object.create(    Error.prototype,    {name: {value: 'CustomError', enumerable: false}});

Example Usage:

try {    throw new CustomError('Something Unexpected Happened!', 1234, 'neat');} catch (ex) {    console.log(ex.name); //CustomError    console.log(ex.message); //Something Unexpected Happened!    console.log(ex.param1); //1234    console.log(ex.param2); //neat    console.log(ex.stack); //stacktrace    console.log(ex instanceof Error); //true    console.log(ex instanceof CustomError); //true}

For environments that require a polyfil of setPrototypeOf:

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {    obj.__proto__ = proto;    return obj;};
answeredJun 20, 2014 at 21:57

3 Comments

as is documented in my answer, this solution can cause a problem in Firefox or other browsers that only log the first line of the stack trace in the console
This is the only answer I've found that works well with ES5 (using ES6 classes works well too). Errors display much more nicely in Chromium DevTools than other answers.
Note: if you are using this solution with TypeScript, you must runthrow CustomError('err') instead ofthrow new CustomError('err')
9

In the above exampleError.apply (alsoError.call) doesn't do anything for me (Firefox 3.6/Chrome 5). A workaround I use is:

function MyError(message, fileName, lineNumber) {    var err = new Error();    if (err.stack) {        // remove one stack level:        if (typeof(Components) != 'undefined') {            // Mozilla:            this.stack = err.stack.substring(err.stack.indexOf('\n')+1);        }        else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {            // Google Chrome/Node.js:            this.stack = err.stack.replace(/\n[^\n]*/,'');        }        else {            this.stack = err.stack;        }    }    this.message    = message    === undefined ? err.message    : message;    this.fileName   = fileName   === undefined ? err.fileName   : fileName;    this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;}MyError.prototype = new Error();MyError.prototype.constructor = MyError;MyError.prototype.name = 'MyError';
answeredAug 21, 2010 at 0:36
panzi's user avatar

Comments

8

In Node as others have said, it's simple:

class DumbError extends Error {    constructor(foo = 'bar', ...params) {        super(...params);        if (Error.captureStackTrace) {            Error.captureStackTrace(this, DumbError);        }        this.name = 'DumbError';        this.foo = foo;        this.date = new Date();    }}try {    let x = 3;    if (x < 10) {        throw new DumbError();    }} catch (error) {    console.log(error);}
answeredJun 23, 2019 at 15:19
Muhammad Umer's user avatar

1 Comment

This is a carbon copy of the MDN sample code presented within their Custom ES6 error section (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…), though you did take the time to modify "CustomError" to :DumbError" and to update the trigger code into something entirely nonsensical. This is a huge relief to me, since I've wondered for some time (and since you've made it clear how simple it is): What's the point of their (and by extension, of course, your) setting ofthis.date andthis.foo? Oh! & how do you applycaptureStackTrace?
7

This isn't that complicated, but I personally find it the easiest way to extend an error easily.

export default class ExtendableError extends Error {    constructor(message) {        super(message);        this.name = this.constructor.name;    }}

Create a utility class like so calledExtendableError. The purpose of this utility class is to be like the normalError class, but change thename property to the name of the class by default, so it's really easy to extend an error.

Now, if you want to extend an error, it only takes one line.

class MyError extends ExtendableError {}
answeredAug 13, 2021 at 16:55
Corman's user avatar

2 Comments

In fairness, and keep in mind: this question was asked 12 years before you answered.extends,class, andsuper were all added in ES2015, several years after OP's initial query. Not knocking your approach; just tossing in a word of defense for some of the the other answers is all. Speaking as someone who's been a full-time FED since '96, let me assure you: there's a LOT of stuff that's WAY less complicated now, lol.
Be aware thatthis.constructor.name cannot be used with bundlers that mangle class or function names, because the error class name in the minified code might be shortened.
3

I just want to add to what others have already stated:

To make sure that the custom error class shows up properly in the stack trace, you need to set the custom error class's prototype's name property to the custom error class's name property.This is what I mean:

CustomError.prototype = Error.prototype;CustomError.prototype.name = 'CustomError';

So the full example would be:

    var CustomError = function(message) {        var err = new Error(message);        err.name = 'CustomError';        this.name = err.name;        this.message = err.message;        //check if there is a stack property supported in browser        if (err.stack) {            this.stack = err.stack;        }        //we should define how our toString function works as this will be used internally        //by the browser's stack trace generation function        this.toString = function() {           return this.name + ': ' + this.message;        };    };    CustomError.prototype = new Error();    CustomError.prototype.name = 'CustomError';

When all is said and done, you throw your new exception and it looks like this (I lazily tried this in the chrome dev tools):

CustomError: Stuff Happened. GASP!    at Error.CustomError (<anonymous>:3:19)    at <anonymous>:2:7    at Object.InjectedScript._evaluateOn (<anonymous>:603:39)    at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)    at Object.InjectedScript.evaluate (<anonymous>:481:21)
answeredFeb 11, 2014 at 4:41
Gautham C.'s user avatar

2 Comments

Doesn't this overwrite the name property forALL Error instances?
@panzi you are correct. I have fixed my small bug. Thanks for the heads up!
3

My 2 cents:

Why another answer?

a) Because accessing theError.stack property (as in some answers) have a large performance penalty.

b) Because it is only one line.

c) Because the solution athttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error does not seems to preserve stack info.

//MyError class constructorfunction MyError(msg){    this.__proto__.__proto__ = Error.apply(null, arguments);};

usage example

http://jsfiddle.net/luciotato/xXyeB/

What does it do?

this.__proto__.__proto__ isMyError.prototype.__proto__, so it is setting the__proto__ FOR ALL INSTANCESof MyError to a specific newly created Error. It keeps MyError class properties and methods and also puts the new Error properties (including .stack) in the__proto__ chain.

Obvious problem:

You can not have more than one instance of MyError with useful stack info.

Do not use this solution if you do not fully understand whatthis.__proto__.__proto__= does.

answeredJun 29, 2014 at 21:40
Lucio M. Tato's user avatar

Comments

2

Since JavaScript Exceptions are difficult to sub-class, I don't sub-class. I just create a new Exception class and use an Error inside of it. I change the Error.name property so that it looks like my custom exception on the console:

var InvalidInputError = function(message) {    var error = new Error(message);    error.name = 'InvalidInputError';    return error;};

The above new exception can be thrown just like a regular Error and it will work as expected, for example:

throw new InvalidInputError("Input must be a string");// Output: Uncaught InvalidInputError: Input must be a string

Caveat: the stack trace is not perfect, as it will bring you to where the new Error is created and not where you throw. This is not a big deal on Chrome because it provides you with a full stack trace directly in the console. But it's more problematic on Firefox, for example.

answeredMay 2, 2014 at 20:44
Jonathan Benn's user avatar

6 Comments

This fails in the casem = new InvalidInputError(); dontThrowMeYet(m);
@Eric I agree, but this seems like a pretty small limitation. I've never needed to instantiate an exception object ahead of time (excepting meta-programming uses like my code sample above). Is this really an issue for you?
Yes, the behaviour seems to be the same, so I will change my answer. I'm not 100% satisfied with the stack trace, which brings you to the "var error" line on Firefox and Chrome
@JonathanBenn I'm real late to the party, so maybe you've already picked this up. I frequently instantiate an exception object when I use asynchronous programming and Promises. Following @Eric's names, I often usem = new ... thenPromise.reject(m). It's no necessity, but the code is easier to read.
@JonathanBenn: (he he) in Oct 14, you seemed to think instantiating an exception object before throwing it would be rare. I gave an example of one time I do it. I won't say it's common, but it's handy to have when I want it. And, my code is more readable because the instantiating is all on one line and the rejecting all on another. I hope that does it!
|
2

As pointed out in Mohsen's answer, in ES6 it's possible to extend errors using classes. It's a lot easier and their behavior is more consistent with native errors...but unfortunately it's not a simple matter to use this in the browser if you need to support pre-ES6 browsers. See below for some notes on how that might be implemented, but in the meantime I suggest a relatively simple approach that incorporates some of the best suggestions from other answers:

function CustomError(message) {    //This is for future compatibility with the ES6 version, which    //would display a similar message if invoked without the    //`new` operator.    if (!(this instanceof CustomError)) {        throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");    }    this.message = message;    //Stack trace in V8    if (Error.captureStackTrace) {       Error.captureStackTrace(this, CustomError);    }    else this.stack = (new Error).stack;}CustomError.prototype = Object.create(Error.prototype);CustomError.prototype.name = 'CustomError';

In ES6 it's as simple as:

class CustomError extends Error {}

...and you can detect support for ES6 classes withtry {eval('class X{}'), but you'll get a syntax error if you attempt to include the ES6 version in a script that's loaded by older browsers. So the only way to support all browsers would be to load a separate script dynamically (e.g. via AJAX oreval()) for browsers that support ES6. A further complication is thateval() isn't supported in all environments (due to Content Security Policies), which may or may not be a consideration for your project.

So for now, either the first approach above or simply usingError directly without trying to extend it seems to be the best that can practically be done for code that needs to support non-ES6 browsers.

There is one other approach that some people might want to consider, which is to useObject.setPrototypeOf() where available to create an error object that's an instance of your custom error type but which looks and behaves more like a native error in the console (thanks toBen's answer for the recommendation). Here's my take on that approach:https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8. But given that one day we'll be able to just use ES6, personally I'm not sure the complexity of that approach is worth it.

answeredDec 27, 2016 at 3:18
Matt Browne's user avatar

Comments

2

Mohsen has a great answer above in ES6 that sets the name, but if you're using TypeScript or if you're living in the future where hopefully thisproposal for public and private class fields has moved past stage 3 as a proposal and made it into stage 4 as part of ECMAScript/JavaScript then you might want to know this is then just a little bit shorter. Stage 3 is where browsers start implementing features, so if your browser supports it the code below just might work. (Tested in the new Edge browser v81 it seems to work fine). Be warned though this is an unstable feature at the moment and should be used cautiously and you should always check browser support on unstable features. This post is mainly for those future dwellers when browsers might support this. To check support checkMDN andCan I use. It's currently got 66% support across the browser market which is getting there but not that great so if you really want to use it now and don't want to wait either use a transpiler likeBabel or something likeTypeScript.

class EOFError extends Error {   name="EOFError"}throw new EOFError("Oops errored");

Compare this to a nameless error which when thrown will not log it's name.

class NamelessEOFError extends Error {}throw new NamelessEOFError("Oops errored");

answeredMay 17, 2020 at 6:50
John's user avatar

Comments

1

The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way:

function MyError() {    var tmp = Error.apply(this, arguments);    tmp.name = this.name = 'MyError'    this.stack = tmp.stack    this.message = tmp.message    return this}    var IntermediateInheritor = function() {}        IntermediateInheritor.prototype = Error.prototype;    MyError.prototype = new IntermediateInheritor()var myError = new MyError("message");console.log("The message is: '"+myError.message+"'") // The message is: 'message'console.log(myError instanceof Error)                // trueconsole.log(myError instanceof MyError)              // trueconsole.log(myError.toString())                      // MyError: messageconsole.log(myError.stack)                           // MyError: message \n                                                      // <stack trace ...>

The only problems with this way of doing it at this point (i've iterated it a bit) are that

  • properties other thanstack andmessage aren't included inMyError and
  • the stacktrace has an additional line that isn't really necessary.

The first problem could be fixed by iterating through all the non-enumerable properties of error using the trick in this answer:Is it possible to get the non-enumerable inherited property names of an object?, but this isn't supported by ie<9. The second problem could be solved by tearing out that line in the stack trace, but I'm not sure how to safely do that (maybe just removing the second line of e.stack.toString() ??).

answeredAug 31, 2013 at 19:32
B T's user avatar

1 Comment

I made a module that can extend most regular old javascript object, including Errors. Its pretty mature at this pointgithub.com/fresheneesz/proto
1

The snippet shows it all.

function add(x, y) {      if (x && y) {        return x + y;      } else {        /**         *          * the error thrown will be instanceof Error class and InvalidArgsError also         */        throw new InvalidArgsError();        // throw new Invalid_Args_Error();       }    }    // Declare custom error using using Class    class Invalid_Args_Error extends Error {      constructor() {        super("Invalid arguments");        Error.captureStackTrace(this);      }    }    // Declare custom error using Function    function InvalidArgsError(message) {      this.message = `Invalid arguments`;      Error.captureStackTrace(this);    }    // does the same magic as extends keyword    Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);    try{      add(2)    }catch(e){      // true      if(e instanceof Error){        console.log(e)      }      // true      if(e instanceof InvalidArgsError){        console.log(e)      }    }
answeredDec 2, 2018 at 8:31
Amarpreet Singh's user avatar

Comments

1

My proposed solution is to use the.name property of error to distinguish between error types instead ofinstancof

This doesn't exactly answer the question, but I think makes for a reasonable solution, for some scenarios anyway.

The benefit I've seen of being able to have aninstanceof CustomError is that you can do custom handling in your promise catch handler.

For example:

class CustomError extends Error {/** ... **/}axios  .post(url, payload)  .then(data => {    if (!data.loggedIn) throw CustomError("not logged in");    return data;  })  .catch(error => {    if (error instanceof CustomError) {/** custom handling of error*//}    throw error  })

If that's what you're trying to accomplish, you be be well suited by the.name parameter as-well though:

export const ERROR_NOT_LOGGED_IN = "ERROR_NOT_LOGGED_IN";axios  .post(url, payload)  .then(data => {    if (!data.loggedIn) throw Error("not logged in").name=ERROR_NOT_LOGGED_IN ;    return data;  })  .catch(error => {    if (error.name === ERROR_NOT_LOGGED_IN) {/** custom handling of error*//}    throw error  })
answeredJun 7, 2021 at 17:04
Daniel's user avatar

Comments

0

I would take a step back and consider why you want to do that? I think the point is to deal with different errors differently.

For example, in Python, you can restrict the catch statement to only catchMyValidationError, and perhaps you want to be able to do something similar in javascript.

catch (MyValidationError e) {    ....}

You can't do this in javascript. There's only going to be one catch block. You're supposed to use an if statement on the error to determine its type.

catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; }}

I think I would instead throw a raw object with a type, message, and any other properties you see fit.

throw { type: "validation", message: "Invalid timestamp" }

And when you catch the error:

catch(e) {    if(e.type === "validation") {         // handle error    }    // re-throw, or whatever else}
answeredDec 13, 2014 at 4:13
hasen's user avatar

1 Comment

Throwing an object is not a great idea. You have noerror.stack, standard tooling won't work with it, etc etc. A better way would be to add properties to an error instance, e.gvar e = new Error(); e.type = "validation"; ...
0

Custom Error Decorator

This is based onGeorge Bailey's answer, but extends and simplifies the original idea. It is written in CoffeeScript, but is easy to convert to JavaScript. The idea is extend Bailey's custom error with a decorator that wraps it, allowing you to create new custom errors easily.

Note: This will only work in V8. There is no support forError.captureStackTrace in other environments.

Define

The decorator takes a name for the error type, and returns a function that takes an error message, and encloses the error name.

CoreError = (@message) ->    @constructor.prototype.__proto__ = Error.prototype    Error.captureStackTrace @, @constructor    @name = @constructor.nameBaseError = (type) ->    (message) -> new CoreError "#{ type }Error: #{ message }"

Use

Now it is simple to create new error types.

StorageError   = BaseError "Storage"SignatureError = BaseError "Signature"

For fun, you could now define a function that throws aSignatureError if it is called with too many args.

f = -> throw SignatureError "too many args" if arguments.length

This has been tested pretty well and seems to work perfectly on V8, maintaing the traceback, position etc.

Note: Usingnew is optional when constructing a custom error.

answeredMar 11, 2015 at 20:41
Carl Younger's user avatar

Comments

0

if you don't care about performances for errors this is the smallest you can do

Object.setPrototypeOf(MyError.prototype, Error.prototype)function MyError(message) {    const error = new Error(message)    Object.setPrototypeOf(error, MyError.prototype);    return error}

you can use it without new just MyError(message)

By changing the prototype after the constructor Error is called we don't have to set the callstack and message

answeredDec 5, 2019 at 2:10
bormat's user avatar

Comments

Protected question. To answer this question, you need to have at least 10 reputation on this site (not counting theassociation bonus). The reputation requirement helps protect this question from spam and non-answer activity.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.