Implementing instanceof checks for custom typescript Error instances?

Typescript has this instanceof checks on custom errors issue, but it’s not clear what we need to do to get instanceof working. For example for this exception how would we get instanceof working:

    /**
     * @param message The error message
     * @param value The value that violates the constraint
     * @param field The name of the field
     * @param type The expected type for the field
     * @param constraint The name of the constraint violated
     * @param code The application or module code for the error
     */
    export class IsError extends Error {
      constructor(
        public message:string,
        public value: any, 
        public field?:string, 
        public type?: string,
        public constraint?: string,
        public code?:string) {
          super(message);
          this.name = 'IsError';
          Object.setPrototypeOf(this, IsError.prototype);      
      }
    }

This link says we can manually adjust the prototype, which is what I’m doing, but this test is not passing:

it("should be an instance of IsError", () => {
   let error = new IsError("This is an IsError", 'value');
   expect(error instanceof IsError)).toBeTruthy();
});

Correction

I had an additional parenthesis in the test ...IsError)) that I mistook for the test not passing. I updated the test and now it work. So the example IsError implementation in this question is correct.

IIUC this implementation will not not work in ES5, but AFAIK clients that include CoreJS will be ok.

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

What goes wrong in your code (as you correctly pointed out) was an extra parenthesis in your assertion.

Just to be complete, a working and minimal example would look like this:

class CustomError extends Error {
    constructor() {
        super();

        Object.setPrototypeOf(this, CustomError.prototype);
    }
}

let error = new CustomError();

console.log(error instanceof CustomError); // Will log 'true'

Solution 2

Read https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work

Now, a complete implementation of @Mathyn and @TmTron answers:

/**
 * Base Error.
 */
class BaseError extends Error {
  constructor(message?: string) {
    const trueProto = new.target.prototype;
    super(message);
    Object.setPrototypeOf(this, trueProto);
  }
}

class Err1 extends BaseError {}
const e1 = new Err1();
console.log(e1 instanceof Err1); // true
console.log(e1 instanceof Error); // true

class Err2 extends Err1 {}
const e2 = new Err2();
console.log(e2 instanceof Err1); // true
console.log(e2 instanceof Err2); // true
console.log(e2 instanceof Error); // true

class NoBaseErr extends Error {}
const x = new NoBaseErr();
console.log(x instanceof Error); // true
console.log(x instanceof NoBaseErr); // false !!!

Package

import BaseError from 'ts-base-error';

See: https://www.npmjs.com/package/ts-base-error

Solution 3

AFAIK, Object.setPrototypeOf() required es6ref

This code from TypeScript#13965 works for me (in es5)

export class MyError extends Error {
    __proto__: Error;
    constructor(message?: string) {
        const trueProto = new.target.prototype;
        super(message);

        // Alternatively use Object.setPrototypeOf if you have an ES6 environment.
        this.__proto__ = trueProto;
    }
}

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply