这个问题直接类似于TypeScript中的类类型检查

我需要在运行时找出任何类型的变量是否实现了接口。这是我的代码:

interface A{
    member:string;
}

var a:any={member:"foobar"};

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

如果您在typescript游乐场中输入这段代码,最后一行将被标记为错误,“名称A不存在于当前作用域”。但事实并非如此,该名称确实存在于当前作用域中。我甚至可以更改变量声明为var a: a ={成员:"foobar"};没有编辑的抱怨。在浏览网页并找到其他问题后,我将接口更改为类,但我不能使用对象字面量来创建实例。

我想知道A类型是如何消失的,但看看生成的javascript就能解释这个问题:

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

没有将A表示为接口,因此不可能进行运行时类型检查。

我知道javascript作为一种动态语言没有接口的概念。是否有方法对接口进行类型检查?

typescript游乐场的自动完成显示typescript甚至提供了一个方法实现。我怎么使用它?


当前回答

在TypeScript 1.6中,用户定义的类型保护将完成这项工作。

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

正如Joe Yang提到的:从TypeScript 2.0开始,你甚至可以利用带标签的联合类型。

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

它也适用于开关。

其他回答

简单的解决方案,与所选的解决方案有相同的缺点,但这个变体捕捉JS错误,只接受对象作为参数,并有一个有意义的返回值。

interface A{
    member:string;
}

const implementsA = (o: object): boolean => {
    try {
        return 'member' in o;
    } catch (error) {
        return false;
    }
}

const a:any={member:"foobar"};

implementsA(a) && console.log("a implements A");
// implementsA("str"); // causes TS transpiler error

TypeGuards

interface MyInterfaced {
    x: number
}

function isMyInterfaced(arg: any): arg is MyInterfaced {
    return arg.x !== undefined;
}

if (isMyInterfaced(obj)) {
    (obj as MyInterfaced ).x;
}

我想指出的是,TypeScript并没有提供直接的机制来动态测试一个对象是否实现了特定的接口。

相反,TypeScript代码可以使用JavaScript技术来检查对象上是否存在适当的成员集。例如:

var obj : any = new Foo();

if (obj.someInterfaceMethod) {
    ...
}

我知道我偶然发现了一个github包,它正确地解决了这个问题,在我的搜索历史中,我终于找到了它。检查typescript-is -尽管它要求你的代码使用ttypescript编译(我目前正在强迫它与create-react-app一起工作,稍后将在成功/失败时更新),你可以用它做各种疯狂的事情。与ts-validate-type不同,这个包也是主动维护的。

你可以检查某个东西是否是字符串或数字,并将其作为字符串或数字使用,而编译器不会抱怨:

import { is } from 'typescript-is';

const wildString: any = 'a string, but nobody knows at compile time, because it is cast to `any`';

if (is<string>(wildString)) { // returns true
    // wildString can be used as string!
} else {
    // never gets to this branch
}

if (is<number>(wildString)) { // returns false
    // never gets to this branch
} else {
    // Now you know that wildString is not a number!
}

你也可以检查你自己的接口:

import { is } from 'typescript-is';

interface MyInterface {
    someObject: string;
    without: string;
}

const foreignObject: any = { someObject: 'obtained from the wild', without: 'type safety' };

if (is<MyInterface>(foreignObject)) { // returns true
    const someObject = foreignObject.someObject; // type: string
    const without = foreignObject.without; // type: string
}

Typescript中的类型保护:

TS有用于此目的的类型保护。他们是这样定义的:

执行运行时检查以保证类型的表达式 在某种范围内。

这基本上意味着TS编译器在拥有足够的信息时可以将类型缩小到更特定的类型。例如:

function foo (arg: number | string) {
    if (typeof arg === 'number') {
        // fine, type number has toFixed method
        arg.toFixed()
    } else {
        // Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?
        arg.toFixed()
        // TSC can infer that the type is string because 
        // the possibility of type number is eliminated at the if statement
    }
}

回到您的问题,我们还可以将类型保护的概念应用于对象,以确定它们的类型。要为对象定义类型保护,需要定义一个返回类型为类型谓词的函数。例如:

interface Dog {
    bark: () => void;
}

// The function isDog is a user defined type guard
// the return type: 'pet is Dog' is a type predicate, 
// it determines whether the object is a Dog
function isDog(pet: object): pet is Dog {
  return (pet as Dog).bark !== undefined;
}

const dog: any = {bark: () => {console.log('woof')}};

if (isDog(dog)) {
    // TS now knows that objects within this if statement are always type Dog
    // This is because the type guard isDog narrowed down the type to Dog
    dog.bark();
}