【问题标题】:TypeScript - check if object's property is a function with given signatureTypeScript - 检查对象的属性是否是具有给定签名的函数
【发布时间】:2021-06-16 22:31:04
【问题描述】:

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

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

但这给出了“无法调用类型缺少调用签名的表达式。类型'any'没有兼容的调用签名。”错误。我知道来自getProperty() 的属性确实可以是任何类型,但如何确定它是带有(conf: {}): void 签名的函数?

【问题讨论】:

  • 你想要的是this吗?

标签: typescript typechecking function-signature


【解决方案1】:

看起来typeof type guards 不存在函数类型。这似乎是设计使然;请参阅microsoft/TypeScript#2072(该问题与instanceof 类型保护有关,但我猜这是类似的推理)。

但是,TypeScript 确实有 user-defined type guards,这意味着您可以编写一个函数,以任何您喜欢的方式缩小其参数的类型。


当代码编译成 JavaScript 时,TypeScript 的静态类型系统是 erased。在运行时,没有可以检查的 (conf: {}) =&gt; void 这样的东西。如果你想编写一个运行时测试来区分 (conf: {}) =&gt; void 类型的值和其他类型的值,你只能做到这一点。

你可以测试typeof x === "function"。但这只是告诉你它是一个函数。它指出函数有多少参数以及它们的类型。您还可以检查x.length === 1 是否需要一个参数,尽管Function.length 周围有一些警告,涉及其余参数和默认参数。但现在你有点卡住了。

在运行时,任何关于函数参数类型的信息都将被删除,如果这些函数最初来自 TypeScript 代码的话。最多您可以通过使用一些测试参数调用该函数并检查是否有问题来“探测”该函数。但这是一个具有可能副作用的破坏性测试,违背了在调用函数之前检查函数类型是否正确的目的。


也许你对这个限制没问题,只是假设length1 的函数是一个足够好的测试。或者您可能有其他一些测试(例如,您可以添加一个名为 isOfRightType 的属性,其值为 true 到您关心的所有此类功能,然后只测试该属性。这通过引入可能性来消除误报的假阴性)。一旦你知道了你的运行时测试,你就可以做一个类型保护:

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

Playground link to code

【讨论】:

    【解决方案2】:

    在你的函数签名中有一个错误,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({}); 
    }
    

    【讨论】:

      猜你喜欢
      • 2019-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-03
      • 2018-05-21
      相关资源
      最近更新 更多