首页 文章

使用Typescript检查接口类型

提问于
浏览
134

这个问题直接类比于Class type check with TypeScript

我需要在运行时找出类型为any的变量实现接口 . 这是我的代码:

interface A{
    member:string;
}

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

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

如果您在typescript playground中输入此代码,则最后一行将被标记为错误"The name A does not exist in the current scope" . 但事实并非如此,该名称确实存在于当前范围内 . 我甚至可以在没有编辑投诉的情况下将变量声明更改为 var a:A={member:"foobar"}; . 在浏览网页并在SO上找到另一个问题后,我将界面更改为类,但后来我无法使用对象文字来创建实例 .

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

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

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

我知道javascript作为动态语言没有接口的概念 . 有没有办法输入接口检查?

打字稿操场的自动完成显示,打字稿甚至提供了方法 implements . 我怎么用呢?

9 回答

  • 55

    您可以在没有 instanceof 关键字的情况下实现您想要的,因为您现在可以编写自定义类型保护:

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

    很多会员

    如果您需要检查很多成员以确定对象是否与您的类型匹配,您可以改为添加一个鉴别器 . 以下是最基本的示例,并要求您管理自己的鉴别器......您需要深入了解模式以确保避免重复的鉴别器 .

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

    在TypeScript 1.6中,user-defined type guard将完成这项工作 .

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

    它也适用于 switch .

  • 2

    typescript 2.0引入了标记联合

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

    用户定义的类型防护怎么样? 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();
    }
    
  • 2

    现在可以了,我刚刚发布了一个增强版的 TypeScript 编译器,它提供了全反射功能 . 您可以从其元数据对象中实例化类,从类构造函数中检索元数据并在运行时检查接口/类 . 你可以看看here

    用法示例:

    在其中一个打字稿文件中,创建一个接口和一个实现它的类,如下所示:

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

    用reflec-ts编译并启动它:

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

    有关 Interface 元类型详细信息,请参阅reflection.d.ts.

    更新:你可以找到一个完整的工作示例here

  • 24

    我想指出TypeScript没有提供动态测试对象是否实现特定接口的直接机制 .

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

    var obj : any = new Foo();
    
    if (obj.someInterfaceMethod) {
        ...
    }
    
  • 13

    TypeGuards

    interface MyInterfaced {
        x: number
    }
    
    function isMyInterfaced(arg: any): arg is MyInterfaced {
        return arg.x !== undefined;
    }
    
    if (isMyInterfaced(obj)) {
        (obj as MyInterfaced ).x;
    }
    
  • 104

    这是另一个选项:模块ts-interface-builder提供了一个构建时工具,可以将TypeScript接口转换为运行时描述符,ts-interface-checker可以检查对象是否满足它 .

    对于OP的例子,

    interface A {
      member: string;
    }
    

    你首先运行 ts-interface-builder ,它会生成一个带有描述符的新简明文件,比如 foo-ti.ts ,您可以这样使用:

    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"
    

    您可以创建单行类型保护功能:

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

    与上面使用user-defined guards相同,但这次使用箭头函数谓词

    interface A {
      member:string;
    }
    
    const check = (p: any): p is A => p.hasOwnProperty('member');
    
    var foo: any = { member: "foobar" };
    if (check(foo))
        alert(foo.member);
    

相关问题