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


当前回答

基于Fenton的回答,下面是我实现的一个函数,用于验证给定对象是否具有接口所具有的键(全部或部分)。

根据您的用例,您可能还需要检查每个接口属性的类型。下面的代码不会这样做。

function implementsTKeys<T>(obj: any, keys: (keyof T)[]): obj is T {
    if (!obj || !Array.isArray(keys)) {
        return false;
    }

    const implementKeys = keys.reduce((impl, key) => impl && key in obj, true);

    return implementKeys;
}

用法示例:

interface A {
    propOfA: string;
    methodOfA: Function;
}

let objectA: any = { propOfA: '' };

// Check if objectA partially implements A
let implementsA = implementsTKeys<A>(objectA, ['propOfA']);

console.log(implementsA); // true

objectA.methodOfA = () => true;

// Check if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);

console.log(implementsA); // true

objectA = {};

// Check again if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);

console.log(implementsA); // false, as objectA now is an empty object

其他回答

下面是我使用类和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)) { ... }

基于Fenton的回答,下面是我实现的一个函数,用于验证给定对象是否具有接口所具有的键(全部或部分)。

根据您的用例,您可能还需要检查每个接口属性的类型。下面的代码不会这样做。

function implementsTKeys<T>(obj: any, keys: (keyof T)[]): obj is T {
    if (!obj || !Array.isArray(keys)) {
        return false;
    }

    const implementKeys = keys.reduce((impl, key) => impl && key in obj, true);

    return implementKeys;
}

用法示例:

interface A {
    propOfA: string;
    methodOfA: Function;
}

let objectA: any = { propOfA: '' };

// Check if objectA partially implements A
let implementsA = implementsTKeys<A>(objectA, ['propOfA']);

console.log(implementsA); // true

objectA.methodOfA = () => true;

// Check if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);

console.log(implementsA); // true

objectA = {};

// Check again if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);

console.log(implementsA); // false, as objectA now is an empty object

我知道这个问题有点老了,但这只是我的五毛钱。这招对我很管用:

const container: Container = icc.controlComponent as unknown as Container;
if (container.getControlComponents) {
    this.allControlComponents.push(...container.getControlComponents());
}

容器是接口,而icc。controlComponent是我想检查的对象,getControlComponents是来自Container接口的一个方法。

使用字符串文字是很困难的,因为如果你想重构你的方法或接口名称,那么你的IDE可能不重构这些字符串文字。 我为您提供我的解决方案,如果在接口中至少有一个方法

export class SomeObject implements interfaceA {
  public methodFromA() {}
}

export interface interfaceA {
  methodFromA();
}

检查object是否为interface类型:

const obj = new SomeObject();
const objAsAny = obj as any;
const objAsInterfaceA = objAsAny as interfaceA;
const isObjOfTypeInterfaceA = objAsInterfaceA.methodFromA != null;
console.log(isObjOfTypeInterfaceA)

注意:即使我们删除了'implements interfaceA',我们也会得到true,因为SomeObject类中仍然存在该方法

你可以在没有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);
}