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?
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?
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
See https://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 display Basic 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;
}
}
On addition to standard message property, JavaScript now supports adding specific cause of the error as a optional param to the Error constructor:
const error1 = new Error('Error one');
const error2 = new Error('Error two', { cause: error1 });
// error2.cause === error1
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);
}
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 classes
class SubCustomError extends CustomError{}
// Tests
console.log(new SubCustomError instanceof CustomError) // true
console.log(new SubCustomError instanceof CustomError) // true
console.log(new CustomError instanceof Error) // true
console.log(new SubCustomError instanceof Error) // true
throw new SubCustomError ('test error')
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 called ExtendableError. The purpose of this utility class is to be like the normal Error class, but change the name 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 {}
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 this proposal 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 check MDN and Can 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 like Babel or something like TypeScript.
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");
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)
}
}
My proposed solution is to use the .name property of error to distinguish between error types instead of instancof
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 an instanceof 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
})
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