Search code examples
typescriptinheritanceextendsinstanceof

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


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
                };
            }
       }


Solution

  • 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.