这个问题直接类似于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甚至提供了一个方法实现。我怎么使用它?


当前回答

另一种解决方案可能与HTMLIFrameElement接口的情况类似。如果我们知道在另一个模块中有它的实现,我们可以通过在接口旁边创建一个对象来声明一个同名的变量。

declare var HTMLIFrameElement: {
    prototype: HTMLIFrameElement;
    new(): HTMLIFrameElement;
};

在这种情况下

interface A {
    member:string;
}

declare var A : {
    prototype: A;
    new(): A;
};

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

应该没问题

其他回答

在Typescript中使用Reflect进行类型保护

下面是一个来自我的Typescript游戏引擎的类型保护的例子

 export interface Start {
    /**
     * Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.
     */
     start(): void
 }


/**
  * User Defined Type Guard for Start
  */
 export const implementsStart = (arg: any): arg is Start => {
     return Reflect.has(arg, 'start')
 } 


 /**
  * Example usage of the type guard
  */

 start() {
    this.components.forEach(component => {
        if (implementsStart(component)) {
            component.start()
        }  

    })
}

我在filter-descriptor.interface.d.ts文件中的@progress/kendo-data-query中找到了一个例子

检查程序

declare const isCompositeFilterDescriptor: (source: FilterDescriptor | CompositeFilterDescriptor) => source is CompositeFilterDescriptor;

示例使用

const filters: Array<FilterDescriptor | CompositeFilterDescriptor> = filter.filters;

filters.forEach((element: FilterDescriptor | CompositeFilterDescriptor) => {
    if (isCompositeFilterDescriptor(element)) {
        // element type is CompositeFilterDescriptor
    } else {
        // element type is FilterDescriptor
    }
});

你也可以向子组件发送多个输入,其中一个是鉴别器,另一个是实际数据,并检查子组件中的鉴别器,如下所示:

@Input() data?: any;
@Input() discriminator?: string;

ngOnInit(){
    if(this.discriminator = 'InterfaceAName'){
      //do stuff
    }
    else if(this.discriminator = 'InterfaceBName'){
      //do stuff
    }
}

显然,你可以把它移动到任何它适用的地方,比如ngOnChanges函数或setter函数,但这个想法仍然成立。如果你想要一个响应式表单,我还建议尝试将ngModel绑定到输入数据上。你可以使用这些if语句根据传入的数据来设置ngModel,并在html中反映:

<div [(ngModel)]={{dataModel}}>
    <div *ngFor="let attr of (data | keyvalue)">
        <!--You can use attr.key and attr.value in this situation to display the attributes of your interface, and their associated values from the data -->
    </div>
</div>

或者用这个代替:

<div *ngIf = "model == 'InterfaceAName'">
    <div>Do This Stuff</div>
</div>
<div *ngIf= "model == 'IntefaceBName'">
    <div>Do this instead</div>
</div>

(您可以使用attr。键和attr。值在这种情况下显示接口的属性,以及它们从数据中关联的值)

我知道这个问题已经有了答案,但我认为这可能对试图构建半模糊的角形式的人有用。你也可以将此用于角材料模块(例如对话框),通过数据参数发送两个变量——一个是你的实际数据,另一个是判别器,并通过类似的过程检查它。最终,这将允许您创建一个表单,并围绕流入其中的数据塑造表单。

另一种解决方案可能与HTMLIFrameElement接口的情况类似。如果我们知道在另一个模块中有它的实现,我们可以通过在接口旁边创建一个对象来声明一个同名的变量。

declare var HTMLIFrameElement: {
    prototype: HTMLIFrameElement;
    new(): HTMLIFrameElement;
};

在这种情况下

interface A {
    member:string;
}

declare var A : {
    prototype: A;
    new(): A;
};

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

应该没问题

下面是我使用类和lodash想出的解决方案:(它有效!)

// TypeChecks.ts
import _ from 'lodash';

export class BakedChecker {
    private map: Map<string, string>;

    public constructor(keys: string[], types: string[]) {
        this.map = new Map<string, string>(keys.map((k, i) => {
            return [k, types[i]];
        }));
        if (this.map.has('__optional'))
            this.map.delete('__optional');
    }

    getBakedKeys() : string[] {
        return Array.from(this.map.keys());
    }

    getBakedType(key: string) : string {
        return this.map.has(key) ? this.map.get(key) : "notfound";
    }
}

export interface ICheckerTemplate {
    __optional?: any;
    [propName: string]: any;
}

export function bakeChecker(template : ICheckerTemplate) : BakedChecker {
    let keys = _.keysIn(template);
    if ('__optional' in template) {
        keys = keys.concat(_.keysIn(template.__optional).map(k => '?' + k));
    }
    return new BakedChecker(keys, keys.map(k => {
        const path = k.startsWith('?') ? '__optional.' + k.substr(1) : k;
        const val = _.get(template, path);
        if (typeof val === 'object') return val;
        return typeof val;
    }));
}

export default function checkType<T>(obj: any, template: BakedChecker) : obj is T {
    const o_keys = _.keysIn(obj);
    const t_keys = _.difference(template.getBakedKeys(), ['__optional']);
    return t_keys.every(tk => {
        if (tk.startsWith('?')) {
            const ak = tk.substr(1);
            if (o_keys.includes(ak)) {
                const tt = template.getBakedType(tk);
                if (typeof tt === 'string')
                    return typeof _.get(obj, ak) === tt;
                else {
                    return checkType<any>(_.get(obj, ak), tt);
                }
            }
            return true;
        }
        else {
            if (o_keys.includes(tk)) {
                const tt = template.getBakedType(tk);
                if (typeof tt === 'string')
                    return typeof _.get(obj, tk) === tt;
                else {
                    return checkType<any>(_.get(obj, tk), tt);
                }
            }
            return false;
        }
    });
}

自定义类:

// MyClasses.ts

import checkType, { bakeChecker } from './TypeChecks';

class Foo {
    a?: string;
    b: boolean;
    c: number;

    public static _checker = bakeChecker({
        __optional: {
            a: ""
        },
        b: false,
        c: 0
    });
}

class Bar {
    my_string?: string;
    another_string: string;
    foo?: Foo;

    public static _checker = bakeChecker({
        __optional: {
            my_string: "",
            foo: Foo._checker
        },
        another_string: ""
    });
}

在运行时检查类型:

if (checkType<Bar>(foreign_object, Bar._checker)) { ... }