TypeScript instanceof not working

24,744

Solution 1

instanceof will return true only if it matches the function or class from which it was constructed. The item here is a plain Object.

const a = { a: 1 } // plain object
console.log(a);

// {a:1}                 <-- the constructor type is empty
//   a: 1
//   __proto__: Object   <-- inherited from

a instanceof A         // false because it is a plain object
a instanceof Object    // true because all object are inherited from Object

If it is constructed using a constructor function or a class, then instanceof will work as expected:

function A(a) {
    this.a = a;
}

const a = new A(1);    // create new "instance of" A
console.log(a);

// A {a:1}               <-- the constructor type is `A`

a instanceof A         // true because it is constructed from A
a instanceof Object    // true

If Goal is an Interface it will only check the structure of the object not its type. If Goal is a constructor then it should return true for instanceof checks.

Try something like:

// interface Goal {...}
class Goal {...}        // you will have to change the way it works.

items = [
   new Goal()
];

Update 2021:

Been playing with Typescript recently and came up with a better solution that works both in Typescript and JavaScript:

Try something like:

interface Goal {
    getCount(): number;
}

class Goal implements Goal {
    getCount() {
        return 3;
    }
}

function getItemCount(item: Goal | Note | Task) {
    return item instanceof Goal ? item.getCount() : 'not a goal';
}

console.log(getItemCount(new Goal()));  // 3
console.log(getItemCount('goal'));      // 'not a goal';

Here the interface and the class has the same name, so they can be used as both and type and an instance checker.

Changing the interface Goal signature or class Goal signature would throw something like:

TS2394: This overload signature is not compatible with its implementation signature.

Solution 2

You can also use type guards to your advantage:

https://basarat.gitbooks.io/typescript/docs/types/typeGuard.html

https://www.typescriptlang.org/docs/handbook/advanced-types.html

For instance, if you use a literal type guard to your class:

class Goal {
 type: 'goal'
...
}

then the check is as simple as :

if (item.type === 'goal') {
}

Or you could write your own type guards:

function isNote(arg: any): arg is Note {
    // because only your Note class has "content" property?
    return arg.content !== undefined;
}

if (isNote(item)) {
    result = { id: index, title: item.content.text };
}

Solution 3

as @Gopikrishna pointed out, object parsed from JSON.parse or received from API will not match your custom Class because it is not created via new operator of desired class.

One workaround is to first cast object to the desired class and then check for a property against undefined.

class User{
displayName:string;
id:string;
}

const user = obj as User;
if (user.displayName!==undefined) {
    //do your thing here
}
Share:
24,744

Related videos on Youtube

AnimaSola
Author by

AnimaSola

Updated on October 13, 2021

Comments

  • AnimaSola
    AnimaSola over 2 years

    I'm having issues using the instanceof operator and it doesn't seem to work. Here is a part of my code:

            const results = _.map(items, function(item: Goal|Note|Task, index: number) { 
                let result = {};
                if (item instanceof Goal) {
                    result = { id: index, title: item.name };
                } else if (item instanceof Note) {
                    result = { id: index, title: item.content.text };
                } else if (item instanceof Task) {
                    result = { id: index, title: item.name };
                }
    
                console.log(item);
                console.log(item instanceof Goal);
                console.log(item instanceof Note);
                console.log(item instanceof Task);
    
                return result; 
            });
    

    All of my logs say false, here is what the console looks like:

    No type matched

    None of them match, despite being explicit that only the 3 types would be possible. You could also see the object itself with a typename of Goal, so I don't get why it doesn't match with instanceof Goal.

    Any ideas?

    • Mike Cluck
      Mike Cluck over 6 years
      How are you generating items? Are they created through constructors? If not, they won't be instances of a given class.
    • Wazner
      Wazner over 6 years
      Did you copy the object perhaps? Through JSON.parse or Object.assign?
    • AnimaSola
      AnimaSola over 6 years
      They are responses from an API/http call. Must by why their typeofs are always objects as opposed to specific types?
    • Mike Cluck
      Mike Cluck over 6 years
      @AnimaSola Right. For instanceof to work, you need to actual make them from constructors. Otherwise they're just objects that happen to have the same shape as your desired objects.
    • AnimaSola
      AnimaSola over 6 years
      Thanks @MikeC, opted to use hasOwnProperty instead.
    • toskv
      toskv over 6 years
      @AnimaSola or just use typeguards. :)
    • Jeff Fischer
      Jeff Fischer over 5 years
      I don't consider any of the solutions on StackOverflow to be a comprehensive solution to the problem. So, I created an npm package angular-http-deserializer for this: npmjs.com/package/angular-http-deserializer#usage
  • mcv
    mcv about 5 years
    Sadly, I've got a case where typeof fails for objects that have been created with a constructor. Logging them will show the correct type, yet the check still fails.
  • Gopikrishna S
    Gopikrishna S about 5 years
    @mcv typeof does not give you the type in JavaScript, typeof only works for primitive types, meaning you will get Object as the result for any type checks. You might want to try comparing obj.constructor. Here is, constructor reference and typeof reference
  • CularBytes
    CularBytes about 5 years
    You would still have to set the type when the object is loaded from the API. But it is a bit of a easier solution then the others provide.
  • CularBytes
    CularBytes about 5 years
    It explains how it works, sure. But it doesn't provide a solution?
  • mcv
    mcv about 5 years
    @GopikrishnaS That was a typo on my part; I did mean instanceof, not typeof. The reason it failed in my case what that a different class of the same name existed in the project, and somehow that one got imported in that file instead of the one I meant. (I've since refactored the two different-but-similar data models into a single unambiguous one.)
  • LuckyLikey
    LuckyLikey almost 5 years
    @CularBytes the answer is: Use the constructor, or instanceof wont work.
  • Gopikrishna S
    Gopikrishna S almost 5 years
    @CularBytes the question was, or rather the statement was "instanceof not working" and I explained "how it actually works" and the solution in the second part "Try something like:"