instanceof operator and this.constructor.name showing parent class name even it is child class in typescipt

367 Views Asked by At

Instanceof operator and this.constructor.name showing parent class name even it is child class in typescipt.

I defined a class that extends default typescript error class

export default class ExpressError extends Error {
    message: string;
    status: number;
    error_id: number;
    no_db_store: boolean;
    name = "ExpressError";

    constructor(message: string, status: number, error_id = 0, no_db_store = true) {
        super();
        this.message = message;
        this.status = status;
        this.error_id = error_id;
        this.no_db_store = no_db_store;

        console.log('\n\n\n\nthis.constructor.name = ' + this.constructor.name); // prints "Error"
        console.log('new.target.name = ' + new.target.name); // prints "ExpressError"
        console.log('\n\n\n\n');
    }
}

Whenever I throw a new ExpressError and try to get the class name it shows "Error" not "ExpressError". Furthermore, it enters first else if. how can I know if I made throw new Error vs throw new ExpressError?

this is the code where I try to get the class name:

        catch (e: unknown) {
            let structured_error: {
                [key: string]: string | undefined | number | boolean
            } = {};

            // this line prints "Error"
            console.log(e.constructor.name)

            // checking if this is a sequelize error
            if (e instanceof  UniqueConstraintError) {
                structured_error = {
                    message: e.errors[0].message,
                    status: 403,
                    stack: e.stack,
                };
            } 

            // this is the matched condition
            else if (e instanceof Error) {
                structured_error = {
                    message: e.message,
                    stack: e.stack,
                };
            }
            else if (e instanceof ExpressError) {
                structured_error = {
                    message: e.message,
                    stack: e.stack,
                    status: e.status,
                    no_db_store: e.no_db_store
                };
            }
       }

2

There are 2 best solutions below

4
Robby Cornelissen On BEST ANSWER

The order of your if/else statements is incorrect. You need to try to match the most specific type first, as an object that is an instance of ExpressError is also an instance of Error.

See the following example:

class ExpressError extends Error {
    message: string;
    status: number;
    error_id: number;
    no_db_store: boolean;
    name = "ExpressError";

    constructor(message: string, status: number, error_id = 0, no_db_store = true) {
        super();
        this.message = message;
        this.status = status;
        this.error_id = error_id;
        this.no_db_store = no_db_store;

        console.log('\n\n\n\nthis.constructor.name = ' + this.constructor.name);
        console.log('new.target.name = ' + new.target.name);
        console.log('\n\n\n\n');
    }
}

function testError() {
    try {
        throw new ExpressError('message', 0);
    } catch (e: any) {
        console.log(e.constructor.name);

        if (e instanceof ExpressError) {
            console.log("Instance of ExpressError");
        }

        if (e instanceof Error) {
            console.log("Instance of Error");
        }
    }
}

testError();

Playground link

The rest of the behavior you describe I can't reproduce. The constructor name is correctly printed.

1
Croquette the Soothsayer On

Here your error variable is both an Error and an ExpressError, it will return true for both of your statements. It is your checking order that is wrong. If you want to differenciate them you should test the derived classes first.

so you should do it like this :

            //match childrens first
            if (e instanceof ExpressError) {
                structured_error = {
                    message: e.message,
                    stack: e.stack,
                    status: e.status,
                    no_db_store: e.no_db_store
                };
            }
            //then finish with the parent
            else if (e instanceof Error) {
                structured_error = {
                    message: e.message,
                    stack: e.stack,
                };
            }