首页 文章

TypeScript - 检查对象的属性是否是具有给定签名的函数

提问于
浏览
2

我有一个从对象获取属性的函数 .

// Utils.ts
export function getProperty<T, K extends keyof T>(obj: T, key: string): T[K] {
    if (key in obj) { return obj[key as K]; }
    throw new Error(`Invalid object member "${key}"`);
}

我想检查返回的属性是否是具有给定签名的函数,然后使用提供的参数调用该属性 .

getProperty() 用于动态获取对象的方法之一并调用它 . 我试过了:

let property: this[keyof this] = utils.getProperty(this, name);
if (typeof property === 'function') ) { property(conf); }

但这会给出"Cannot invoke an expression whose type lacks a call signature. Type 'any' has no compatible call signatures."错误 . 我知道来自 getProperty() 的属性确实可以是任何类型,但如何确定它是 (conf: {}): void 签名的函数?

2 回答

  • 1

    对于函数类型,它看起来不像typeof type guards . 它似乎是by design(那个问题是关于 instanceof 类型的守卫,但我也是类似的推理) .

    但是,TypeScript确实有user-defined type guards,这意味着您可以编写一个函数,以任何方式缩小其参数的类型 . 那么,什么运行时测试将确定某个值 x 是否为 (conf: {}) => void 类型的函数?当然,您可以测试是否 typeof x === "function" . 这并不表示该函数将采用任何特定类型的参数而不返回任何内容 . 也许您只知道从 getProperty 中提取的任何函数都是正确的类型 . 如果没有,您应该弄清楚如何区分运行时的差异 . (是 x.length === 1 ?还有其他一些显着特征吗?)

    一旦了解了运行时测试,就可以创建一个类型保护:

    function isFunctionOfRightType(x: any): x is (conf: {})=>void {
       return (typeof x === 'function') && (x.length === 1); // or whatever test
    }
    
    // ... later
    
    let property: this[keyof this] = utils.getProperty(this, name);
    if (isFunctionOfRightType(property)) { property(conf); } // no error now
    

    希望有所帮助 . 祝好运!

  • 3

    在你的函数签名中有一个错误, key 参数应该是 K 类型,当你使用参数常量时,这将给你更好的推理:

    export function getProperty<T, K extends keyof T>(obj: T, key: string): T[K] {
        if (key in obj) { return obj[key as K]; }
        throw new Error(`Invalid object member "${key}"`);
    }
    
    let foo = {
        fn (conf: {}): void {}
    }
    let fn = getProperty(foo, "fn");
    fn({}); // callable
    

    使用字符串键并且在编译时未验证的问题是编译器无法真正帮助您 . 它会假设,因为您索引我的任意字符串,返回类型可以是目标的任何有效字段类型 . 在运行时无法验证函数参数类型,因为它们将被擦除,您可以验证参数计数:

    let property:  Function = getProperty(this, name) as any;
    if (typeof property === 'function' && property.length == 1)  
    { 
        property({}); 
    }
    

相关问题