这个问题直接类似于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编译器,它提供了完整的反射功能。您可以从类的元数据对象实例化类,从类构造函数检索元数据,并在运行时检查接口/类。你可以在这里查看

使用的例子:

在你的一个typescript文件中,创建一个接口和一个实现它的类,如下所示:

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

class MyClass implements MyInterface {
    counter = 0;

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

现在让我们打印一些已实现接口的列表。

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

使用reflect -ts编译并启动它:

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

有关接口元类型的详细信息,请参阅reflect .d.ts。

更新: 您可以在这里找到一个完整的工作示例

其他回答

export interface ConfSteps {
    group: string;
    key: string;
    steps: string[];
}
private verify(): void {
    const obj = `{
      "group": "group",
      "key": "key",
      "steps": [],
      "stepsPlus": []
    } `;
    if (this.implementsObject<ConfSteps>(obj, ['group', 'key', 'steps'])) {
      console.log(`Implements ConfSteps: ${obj}`);
    }
  }
private objProperties: Array<string> = [];

private implementsObject<T>(obj: any, keys: (keyof T)[]): boolean {
    JSON.parse(JSON.stringify(obj), (key, value) => {
      this.objProperties.push(key);
    });
    for (const key of keys) {
      if (!this.objProperties.includes(key.toString())) {
        return false;
      }
    }
    this.objProperties = null;
    return true;
  }

TypeGuards

interface MyInterfaced {
    x: number
}

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

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

在我看来,这是最好的方法;在接口上附加一个“Fubber”符号。它的编写速度要快得多,对于JavaScript引擎来说,它比类型保护快得多,它支持接口的继承,如果你需要的话,它使类型保护易于编写。

这就是ES6有符号的目的。

接口

// Notice there is no naming conflict, because interfaces are a *type*
export const IAnimal = Symbol("IAnimal"); 
export interface IAnimal {
  [IAnimal]: boolean; // the fubber
}

export const IDog = Symbol("IDog");
export interface IDog extends IAnimal {
  [IDog]: boolean;
}

export const IHound = Symbol("IDog");
export interface IHound extends IDog {
  // The fubber can also be typed as only 'true'; meaning it can't be disabled.
  [IDog]: true;
  [IHound]: boolean;
}

import { IDog, IAnimal } from './interfaces';
class Dog implements IDog {
  // Multiple fubbers to handle inheritance:
  [IAnimal] = true;
  [IDog] = true;
}

class Hound extends Dog implements IHound {
  [IHound] = true;
}

测试

如果你想帮助TypeScript编译器,这段代码可以放在类型保护中。

import { IDog, IAnimal } from './interfaces';

let dog = new Dog();

if (dog instanceof Hound || dog[IHound]) {
  // false
}
if (dog[IAnimal]?) {
  // true
}

let houndDog = new Hound();

if (houndDog[IDog]) {
  // true
}

if (dog[IDog]?) {
  // it definitely is a dog
}

简单的解决方案,与所选的解决方案有相同的缺点,但这个变体捕捉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

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