Home > front end >  Why is instanceof not working as expected in Typescript?
Why is instanceof not working as expected in Typescript?

Time:02-01

So I have these classes that I use to handle errors in different scenerios like so:

export class APIError extends Error {
    public readonly statusCode: number;
    public readonly message: string;

    constructor(statusCode?: number, message?: string) {
        super(message);
        Object.setPrototypeOf(this, APIError.prototype);

        if (typeof statusCode === 'string') {
            message = statusCode;
            statusCode = null;
        }

        this.statusCode = statusCode || 500;
        this.message = message || 'Internal Server Error';
    }

    public toJSON(): JsonData {
        return {
            statusCode: this.statusCode,
            message: this.message,
        };
    }
}

export class NotFound extends APIError {
    constructor(message?: string) {
        super(404, 'Not Found');
        Object.setPrototypeOf(this, NotFound.prototype);
    }
}

export class StreamNotFound extends NotFound {
    constructor() {
        super('Stream Not Found');
        Object.setPrototypeOf(this, StreamNotFound.prototype);
    }
}

And then I have this update abstract method:

public update(id: string, updateThing: T): T {
        if (!updateThing) return;

        const thing: T = this.get(id);
        if (!thing) {
            throw new NotFound(`${this.displayName} could not be found.`);
        }
      ....

In my controller, I'm trying to catch the error and then get it's instance like so:

} catch (e) {
            const statusCode = (e instanceof StreamNotFound) ? 404 : null;
            throw HttpController.handleError(e, statusCode);
        }

But statusCode will always return null, even though streamNotFound extends NotFound, and Notfound is being used by the Update abstract method.

As you can see, I'm adding the Object.setPrototypeOf(this, StreamNotFound.prototype); on each of the methods, so I'm wondering why it is not working as expected?

CodePudding user response:

A subclass will always be an instanceof itself and any of its parent classes. However, the reverse is not true: a parent class is not an instanceof any of its subclasses.

In this example, StreamNotFound instanceof NotFound === true. However, a parent class is explicitly not instanceof any of its subclasses. Here, NotFound instanceof StreamNotFound === false.

In your controller, you're throwing an instance of NotFound, which will never be an instanceof StreamNotFound, as it's further up in the prototype chain than its subclasses.


In the simplified example below, Bar extends Foo as a subclass, thus:

  • Foo instanceof Foo === true
  • Bar instanceof Foo === true
  • Bar instanceof Bar === true
  • Foo instanceof Bar === false

class Foo {
  constructor() {
  
  }
}

class Bar extends Foo {
  constructor() {
    super();
  }
}

const obj1 = new Foo();
const obj2 = new Bar();

console.log("Bar instanceof Bar: "   (obj2 instanceof Bar));
console.log("Bar instanceof Foo: "   (obj2 instanceof Foo));
console.log("Foo instanceof Bar: "   (obj1 instanceof Bar));

  •  Tags:  
  • Related