Is instanceof broken?

Published

We all know what instanceof does. By definition

The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value.

Source

Few examples:

class Foo {
    constructor() {
        this.property = 'test';
    }
}


new Foo() instanceof Foo; // true
{
    property: 'test'
}
instanceof
Foo; // false

Very simple and clear. But as always in reality

When I was developing @pallad/secret and @pallad/config i found that CLI for config didn't handle instances of Secret and couldn't detect instances of Provider. Both of those classes uses instanceof for type check, which seamed to be great option.

The issue was that those modules were installed more than one time on my system. One was global installation and second local from examples folder. Creating an instance from local module does not work properly with instanceof operator within a global module. Who would have thought?

I'm not alone in that problem. The chance of receiving multiple installations of the same module in the project is quite high. Take a look at npm list in some of your projects to see that.

The fix? Just define extra unique non-configurable and non-enumerable property in your object. \

const TYPE_KEY = '@type';
const TYPE = '@mylib/name/Foo';

class Foo {

    constructor() {
        Object.defineProperty(this, TYPE_KEY, {
            value: TYPE,
            configurable: false,
            enumerable: false
        });
    }

    static is(value: any): value is Foo {
        return value instanceof Foo ||
            typeof value === 'object' && value !== null &&
            value[TYPE_KEY] === TYPE
    }
}


You can always use predicates to simplify that or use library created just for this purpose: https://github.com/pallad-ts/type-check


import * as is from 'predicates';

const TYPE_KEY = '@type';
const TYPE = '@mylib/name/Foo';

const IS_TYPE = is.property(TYPE_KEY, is.strictEqual(TYPE));

class Foo {

    constructor() {
        Object.defineProperty(this, TYPE_KEY, {
            value: TYPE,
            configurable: false,
            enumerable: false
        });
    }

    static is(value: any): value is Foo {
        return value instanceof Foo || IS_TYPE(value);
    }
}

Is instanceof bad then?

Absolutely not! It is a great tool for its job. When working on local project where all modules are guaranteed to be installed once in the system or in the project then it is completely fine. However when developing a library that is suppose to be used my many people in various places it is worth to think about it.

Łukasz Kużyński - Wookieb
Thoughts, tips about programming and related subjects to make your job easier and more pleasant so you won't burnout quickly.
Copyright © Łukasz Kużyński - Wookieb 2023 • All rights reserved.