Interface type check with Typescript

Interface type check with Typescript

This question is the direct analogon to Class type check with 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 ?

Related:  Getting all selected checkboxes in an array

Solutions/Answers:

Solution 1:

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

Solution 2:

In TypeScript 1.6, user-defined type guard will do the job.

interface Foo {
    fooProperty: string;
}

interface Bar {
    barProperty: string;
}

function isFoo(object: any): object is Foo {
    return 'fooProperty' in object;
}

let object: Foo | Bar;

if (isFoo(object)) {
    // `object` has type `Foo`.
    object.fooProperty;
} else {
    // `object` has type `Bar`.
    object.barProperty;
}

And just as Joe Yang mentioned: since TypeScript 2.0, you can even take the advantage of tagged union type.

interface Foo {
    type: 'foo';
    fooProperty: string;
}

interface Bar {
    type: 'bar';
    barProperty: number;
}

let object: Foo | Bar;

// You will see errors if `strictNullChecks` is enabled.
if (object.type === 'foo') {
    // object has type `Foo`.
    object.fooProperty;
} else {
    // object has type `Bar`.
    object.barProperty;
}

And it works with switch too.

Related:  Comparison between d3.js and chart.js (only for charts)

Solution 3:

typescript 2.0 introduce tagged union

Typescript 2.0 features

interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {
    // In the following switch statement, the type of s is narrowed in each case clause
    // according to the value of the discriminant property, thus allowing the other properties
    // of that variant to be accessed without a type assertion.
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return Math.PI * s.radius * s.radius;
    }
}

Solution 4:

How about User-Defined Type Guards? https://www.typescriptlang.org/docs/handbook/advanced-types.html

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function isFish(pet: Fish | Bird): pet is Fish { //magic happens here
    return (<Fish>pet).swim !== undefined;
}

// Both calls to 'swim' and 'fly' are now okay.

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}

Solution 5:

It’s now possible, I just released an enhanced version of the TypeScript compiler that provides full reflection capabilities. You can instantiate classes from their metadata objects, retrieve metadata from class constructors and inspect interface/classes at runtime. You can check it out here

Related:  How to randomize (shuffle) a JavaScript array?

Usage example:

In one of your typescript files, create an interface and a class that implements it like the following:

interface MyInterface {
    doSomething(what: string): number;
}

class MyClass implements MyInterface {
    counter = 0;

    doSomething(what: string): number {
        console.log('Doing ' + what);
        return this.counter++;
    }
}

now let’s print some the list of implemented interfaces.

for (let classInterface of MyClass.getClass().implements) {
    console.log('Implemented interface: ' + classInterface.name)
}

compile with reflec-ts and launch it:

$ node main.js
Implemented interface: MyInterface
Member name: counter - member kind: number
Member name: doSomething - member kind: function

See reflection.d.ts for Interface meta-type details.

UPDATE:
You can find a full working example here

Solution 6:

Here’s another option: the module ts-interface-builder provides a build-time tool that converts a TypeScript interface into a runtime descriptor, and ts-interface-checker can check if an object satisfies it.

For OP’s example,

interface A {
  member: string;
}

You’d first run ts-interface-builder which produces a new concise file with a descriptor, say, foo-ti.ts, which you can use like this:

import fooDesc from './foo-ti.ts';
import {createCheckers} from "ts-interface-checker";
const {A} = createCheckers(fooDesc);

A.check({member: "hello"});           // OK
A.check({member: 17});                // Fails with ".member is not a string" 

You can create a one-liner type-guard function:

function isA(value: any): value is A { return A.test(value); }