【问题标题】:Type argument interference doesn't work, but explicit type argument does类型参数干扰不起作用,但显式类型参数可以
【发布时间】:2020-08-30 17:42:20
【问题描述】:

我在尝试实现泛型类型时遇到了一个特殊问题。假设我想声明一个函数的类型,该函数在其参数中返回给定对象的键:

type Dimension = { x: number, y: number };

type MyFunctionGeneric1 = <T>(payload: T) => T[keyof T];
const getX1: MyFunctionGeneric1 = (payload: Dimension) => payload.x; // Doesn't work

type MyFunctionGeneric2<T> = (payload: T) => T[keyof T];
const getX2: MyFunctionGeneric2<Dimension> = (payload) => payload.x; // OK

我的问题是,如果我明确提供有效负载的类型,那么它就可以工作。但是,一旦我尝试让泛型类型隐式推断有效负载类型,它就会抱怨:

Type '(payload: Dimension) => number' is not assignable to type 'MyFunctionGeneric1'.
  Types of parameters 'payload' and 'payload' are incompatible.
    Type 'T' is not assignable to type 'Dimension'

你也可以see it on the playground here

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    第一种情况不起作用的原因是MyFunctionGeneric1描述了一个泛型函数,但显然你的getX1不是泛型函数(它只接受类型Dimension ),而在第二种情况下,MyFunctionGeneric2&lt;T&gt; 本身就是一个泛型类型,不描述泛型函数。

    如果你想让 TypeScript 自动推断T,你可以使用以下技巧:

    type Dimension = { x: number, y: number };
    
    function infer<T>(func: MyFunctionGeneric2<T>): MyFunctionGeneric2<T> {
        return func;
    }
    
    type MyFunctionGeneric2<T> = (payload: T) => T[keyof T];
    const getX2 = infer((payload: Dimension) => payload.x);  // OK
    

    看到这个Playground Link

    【讨论】:

    • 哇,很好的解释。但是对于MyFunctionGeneric1,这不是泛型的用途吗?你定义通用。您可以在实际应用时添加您的特定类型。如果我真的想按照特里写的格式,我该怎么办?我已经花了半个小时,但仍然没有运气。
    • 这取决于您的用例。在 Terry 的情况下,他确实需要一个泛型类型而不是泛型函数的类型,因为他的函数确实不是泛型的。
    • 非常感谢。我想我会改编特里代码的第二种方法。这对我来说似乎更有意义。
    【解决方案2】:

    好的,这是我尝试调试此问题的路径。我首先想到的是尝试将extends {} 添加到类型参数(我的假设是 T 不仅可以是对象,还可以是其他原始类型):

    type MyFunctionGeneric1 = <T extends {}>(payload: T) => T[keyof T];
    const getX1: MyFunctionGeneric1 = (payload: Dimension) => payload.x;
    

    当我这样做时,它开始抱怨:

    Type '(payload: Dimension) => number' is not assignable to type 'MyFunctionGeneric1'.
      Types of parameters 'payload' and 'payload' are incompatible.
        Type 'T' is not assignable to type 'Dimension'.
          Type '{}' is missing the following properties from type 'Dimension': x, y(2322)
    

    于是另一个假设诞生了。如果 TypeScript 无法推断它,因为它没有可用于类型评估的实际数据怎么办?

    为了检查这个假设,我做了这个;

    type MyFunctionGeneric1 = <T>(payload: T) => T[keyof T];
    const getX1: MyFunctionGeneric1 = (payload: Dimension) => payload.x;
    
    getX1({x: 10, y: 20});
    

    虽然在您实际调用该方法时推断类型按预期工作(毕竟我们有数据要推断),但什么都没有改变。

    这样我们可以将问题缩小到与 TypeScript 本身相关的问题,而不是您的代码。

    我的假设是在分配函数和实际调用它时评估类型的方式不同。在您的情况下,您正在为类型为 MyFunctionGeneric 的变量分配一个函数,在此步骤中它无法推断签名和类型。因此,当 TypeScript 评估类型 MyFunctionGeneric 时,它缺少来自右手表达式的类型数据。

    如何处理?

    您可以显式传递返回类型,但是,问题是关于什么的......

    type Dimension = { x: number, y: number };
    type Getter<T> = (payload: T) => T[keyof T]
    
    const getX: Getter<Dimension> = (payload) => payload.x
    

    附:我仍在试图弄清楚为什么它不能推断,但似乎它是 TypeScript 的预期限制。您正在尝试将泛型类型缩小为非泛型。

    【讨论】:

      猜你喜欢
      • 2019-06-24
      • 2021-06-14
      • 1970-01-01
      • 2018-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多