【问题标题】:TypeScript generics - callback function inferenceTypeScript 泛型 - 回调函数推断
【发布时间】:2020-11-05 05:11:44
【问题描述】:

我试图理解为什么 Typescript 能够在泛型是回调的返回类型时推断带有回调参数的函数的返回类型,但当泛型是回调的类型时却不能这样做回调函数。

没有具体的推断

const f = <T extends () => any>(callback: T) => callback()

// inference r: any
const r = f(() => 1)

但推理适用于

const f = <T extends any>(callback: () => T) => callback()

// inference r: number
const r = f(() => 1)

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    我认为这是编译器在对泛型类型执行某些操作时所做的简化。它没有将每个操作表示为可能越来越复杂的泛型类型,而是将泛型类型参数扩展到其约束并使用它。当您使用已知的键对泛型类型对象进行索引时,您会看到这种情况:

    function foo<T extends { a: any }>(obj: T) {
      const a1 = obj.a; // any, why not T['a']?
      const a2: T['a'] = obj.a; // this works though
    }
    

    请参阅microsoft/TypeScript#33181 了解更多信息。在上面,编译器在访问其a 属性之前看到obj.a 并将objT 扩大到{a: any}。所以a1 的类型是any。如果编译器推迟了扩展,它可以将此属性表示为lookup typeT['a']。实际上,如果您将要保存的变量显式注释为T['a'],编译器不会抱怨。

    调用泛型类型的函数似乎也会发生同样的情况(尽管我还没有找到提到这一点的规范文档):

    function bar<T extends () => any>(fn: T) {
      const r1 = fn(); // any, why not ReturnType<T> ?
      const r2: ReturnType<T> = fn(); // this works though
    }
    

    如您所见,r1 的类型为 any,因为编译器在调用之前将 fnT 扩大到其约束 () =&gt; any。如果编译器推迟了扩展,它可以将返回类型表示为ReturnType&lt;T&gt;(请参阅documentation for ReturnType)。同样,如果您手动将值注释为ReturnType&lt;T&gt;,编译器不会抱怨它。

    这使我找到了我认为适合您的解决方案/解决方法:手动注释函数的返回类型:

    const f = <T extends () => any>(callback: T): ReturnType<T> => callback()
    

    编译时没有错误,现在当您在回调中调用 f 时,您会获得更好的返回类型:

    const r = f(() => 1); // number
    

    Playground link to code

    【讨论】:

    • 感谢您的详细回答。 TypeScript 进行这种简化的原因是什么?只是为了加快编译速度吗?
    猜你喜欢
    • 1970-01-01
    • 2020-01-23
    • 2020-09-09
    • 2020-03-12
    • 1970-01-01
    • 1970-01-01
    • 2017-06-02
    • 2020-07-23
    • 1970-01-01
    相关资源
    最近更新 更多