javascripttypescriptinterface

Interface type check with Typescript


This question is the direct analogon to Class type check in TypeScript

I need to find out at runtime if a variable of type any implements an interface. Here's my code:

interface A {
    member: string;
}
    
var a: any = { member: "foobar" };
    
if (a instanceof A) alert(a.member);

If you enter this code in the typescript playground, the last line will be marked as an error, "The name A does not exist in the current scope". But that isn't true, the name does exist in the current scope. I can even change the variable declaration to var a:A={member:"foobar"}; without complaints from the editor. After browsing the web and finding the other question on SO I changed the interface to a class but then I can't use object literals to create instances.

I wondered how the type A could vanish like that but a look at the generated javascript explains the problem:

var a = {
    member: "foobar"
};

if (a instanceof A) {
    alert(a.member);
}

There is no representation of A as an interface, therefore no runtime type checks are possible.

I understand that javascript as a dynamic language has no concept of interfaces. Is there any way to type check for interfaces?

The typescript playground's autocompletion reveals that typescript even offers a method implements. How can I use it ?


Solution

  • You can achieve what you want without the instanceof keyword as you can write custom type guards now:

    interface A {
        member: string;
    }
    
    function instanceOfA(object: any): object is A {
        return 'member' in object;
    }
    
    var a: any = {member: "foobar"};
    
    if (instanceOfA(a)) {
        alert(a.member);
    }
    

    Lots of Members

    If you need to check a lot of members to determine whether an object matches your type, you could instead add a discriminator. The below is the most basic example, and requires you to manage your own discriminators... you'd need to get deeper into the patterns to ensure you avoid duplicate discriminators.

    interface A {
        discriminator: 'I-AM-A';
        member: string;
    }
    
    function instanceOfA(object: any): object is A {
        return object.discriminator === 'I-AM-A';
    }
    
    var a: any = {discriminator: 'I-AM-A', member: "foobar"};
    
    if (instanceOfA(a)) {
        alert(a.member);
    }