【问题标题】:Typescript. Filtering functional properties and `Cannot invoke an expression whose type lacks a call signature.`打字稿。过滤功能属性和“不能调用类型缺少调用签名的表达式。”
【发布时间】:2018-04-28 19:00:15
【问题描述】:

我正在实现一个call 函数,该函数接收对象和属性名称,然后在传递的属性处调用驻留在传递对象中的函数。

function call<
    T,
    K extends {
        [K in keyof T]: T[K] extends Function ? K : never
    }[keyof T],
    >(t: T, func: K) {

    t[func]();
}

看起来 ts 对此很满意并正确过滤了属性(没有prop,只有do 可用):

但它也在我调用函数的行上显示错误:t[func]();:

Cannot invoke an expression whose type lacks a call signature.
Type '{}' has no compatible call signatures.

我发现了类似的issue [Type deduction using mapped types and generics],但是它作为另一个issue [Call signatures of union types] 的副本被关闭了。显然问题是第二个泛型参数实际上是满足条件的所有属性名称的联合类型。

除了转换为any之外,还有其他方法可以解决这个问题吗?

PS:Playground

【问题讨论】:

    标签: typescript


    【解决方案1】:

    Typescript 将无法确定您对属性的过滤将意味着 T[K] 始终是一个函数。您可以稍微反转您的约束,以指定 T 扩展具有 ()=&gt;void 类型属性的内容

    function call<
        T extends { [P in K]: ()=> void },
        K extends keyof T
        >(t: T, func: K) {
    
        t[func]();
    }
    
    class Test {
        prop: string = "";
        doStuff(): void {}
    }
    
    call(new Test(), "prop") // error
    call(new Test(), "doStuff") // ok
    

    请注意,您可能会考虑在原始函数中使用断言,上面的版本在避免断言和类型安全的同时无法帮助 intelisense 确定可以为第二个参数分配哪些可能的值,所以不要这样:

    你得到这个:

    虽然在这种情况下通常应该避免断言,但我们确信类型实际上是正确的,因为我们对键有限制,所以这个版本也应该没问题:

    function call2<
    T,
    K extends {
        [K in keyof T]: T[K] extends ()=> void ? K : never
    }[keyof T],
    >(t: T, func: K) {
    
        (t[func] as any as ()=> void)();
    }
    

    【讨论】:

    • 有趣的把戏。但我更喜欢我的版本)主要是因为如果您传递不适合的属性名称,您会看到错误,而不是像您的情况那样在传递的对象上看到错误。
    猜你喜欢
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 2018-11-11
    • 1970-01-01
    • 2017-09-01
    • 2017-12-19
    • 2021-02-06
    • 2017-07-14
    相关资源
    最近更新 更多