[Strategy pattern] TS doesn't have runtime input type check

Topics: Code Checkins
Sep 10, 2013 at 12:02 PM
Edited Sep 10, 2013 at 12:03 PM
interface X {
    echo();
}

class XA implements X {
    public echo() { console.log("XA"); }
}

class XB implements X {
    public echo() { console.log("XB"); }
}

class NOT_X {
    public echo() { alert("TYPESCRIPT FAILED - NO ANY INTERFACE CHECK"); }
}

function doEcho(who: X) { // input type has no check
    who.echo(); 
}

var arr = [];
arr[0] = new XA();
arr[1] = new XB();
arr[2] = new NOT_X();
arr.forEach(function(v, i, a) { doEcho(v); });
TEST THIS CODE HERE
Sep 11, 2013 at 7:57 PM
Edited Sep 11, 2013 at 8:03 PM
TypeScript is purely a compile-time language. When you declare your array var arr = [], the type of the array is inferred as any[]. So this is equivalent to you having said var arr: any[] = [];. This means that when you later call doEcho(v), the compiler has inferred the type of v to also be any. A type of any means the compiler is essentially shutting off the type system, thus you can pass a parameter of any to a function that accepts parameters of type X. This is legal TypeScript functioning the way it was designed to. TypeScript does not assert any runtime checks.

To get more compile-time safety, you could therefore assert the type of arr by changing it to var arr: X[] = [];. However, the code still compiles, because TypeScript is structurally typed. So even though NOT_X has not explicitly implemented interface X the compiler figures out that it is structurally equivalent to interface X and allows the assignment at arr[2] = new NOT_X();.

If you really want a runtime check on the type of an object, you'll have to insert one yourself.
Sep 12, 2013 at 8:49 AM
Yes I understand that, the question is shall typescript support runtime checks like php or any languages that don't allow to put type A var to function taking type B var?
Coordinator
Sep 13, 2013 at 8:32 PM
Not having runtime overhead is a primary goal for TypeScript. Even if there were some notion of a runtime type system, the TypeScript type system is structural, not nominal like what you're showing here.

You can always add some sort of type metadata yourself:
interface X {
    echo();
    magic_cookie: number; // Must be 4!
}

class XA implements X {
    public echo() { console.log("XA"); }
    magic_cookie = 4;
}

class XB implements X {
    public echo() { console.log("XB"); }
    magic_cookie = 4;
}

class NOT_X {
    public echo() { alert("TYPESCRIPT FAILED - NO ANY INTERFACE CHECK"); }
}

function doEcho(who: X) { // input type has no check
    if(who.magic_cookie !== 4) throw new Error('Oh no!');
    who.echo(); 
}