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