首页 文章

打字稿 . 过滤功能属性和`无法调用类型缺少调用签名的表达式

提问于
浏览
2

我正在实现一个接收对象和属性名称的 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 可用):

enter image description here

但它也在我调用函数的行上显示错误: 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

1 回答

  • 1

    打字稿将无法确定您对属性的过滤意味着 T[K] 始终是一个函数 . 您可以稍微反转约束,以指定 T 扩展具有 ()=>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
    

    但请注意,您可以考虑在原始函数中使用断言,上面的版本同时避免断言并且类型安全无助于知识分子找出可以为第二个参数分配哪些可能的值,因此不是这样:

    enter image description here

    你明白这个:
    enter image description here

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

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

相关问题