【问题标题】:Working with Constrained generic functions confuses me使用受约束的通用函数让我感到困惑
【发布时间】:2023-02-25 20:47:29
【问题描述】:
function getMinimum<T extends {length : number}>(arg1 : T, arg2 : T) : T {
    if (arg1.length >= arg2.length) {
        return arg2;
    }

    else {
        return arg1;
    }
}

let str = "Hello world"; 
const res0 = getMinimum([1, 2, 3], str); // Argument of type 'number[]' is not assignable to parameter of type 'string'
const res1 = getMinimum([1, 2, 3], {length: 12}); // res1 type - number[] | {length: number}

在上面的代码块中,getMinimum([1, 2, 3], {length: 12}) 工作正常但 getMinimum([1, 2, 3], str) 触发类型错误。

我认为,它不应该触发类型错误,并且应该为 res0 分配一种类型:number[] |细绳。但是 Typescript 不会为 res0 这样做,但会为 res1 这样做,为什么会有区别?

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    Typescript 使用泛型不仅可以限制参数的类型,还可以推断提供的参数的类型。

    一种简单的可视化方法是想知道如果用户手动提供泛型的值会是什么样子:getMinimum&lt;number[]&gt;([1, 2, 3], str)。这里更明显的是number[]适合[1, 2, 3]而不是"Hello world"

    因此,当每个参数的类型需要不同时,即使它们满足相同的约束条件,您也需要为每个参数使用不同的泛型:

    type Length = { length : number };
    
    function getMinimum<T extends Length, U extends Length>(arg1 : T, arg2 : U) : T | U {
        if (arg1.length >= arg2.length) {
            return arg2;
        }
    
        else {
            return arg1;
        }
    }
    

    至于为什么 getMinimum([1, 2, 3], { length: 12 }) 通过您的实施而 getMinimum([1, 2, 3], str) 没有:

    一点免责声明:我不是“知情者”,以下是一个心智模型。

    当您将 2 个参数映射到单个泛型时,TS大概执行以下操作:

    • 独立推断每个参数的类型;
    • 检查这些类型是否与约束兼容;
    • 如果不是,则拒绝参数;
    • 如果是,将它们相交以找到共同类型
    • 如果交集产生never,则拒绝第一个参数并在错误中提及第二个参数。

    getMinimum([1, 2, 3], { length: 12 })的情况下:TS推断number[]arg1{length: number}arg2,检查它们是否可分配给{ length: number },然后将它们相交,以number[]结束并接受类型.

    getMinimum([1, 2, 3], str)的情况下:TS推断number[]arg1stringarg2,检查它们是否可分配给{ length: number },然后将它们相交,以never结束并拒绝第一个一。

    有一种通用类型可以满足{ length: number } 并适用于number[]string:即{ length: number } &amp; (string | number[]),但 TS 不会尝试推断该类型。一个可能的原因是,如果这样做,它会扩大泛型的类型,以便它适用于 2 个参数,但我们通常在推断可能的最窄类型之后,因为窄类型更有用。

    类型推断和类型检查之间也应该有所区别:即使类型检查器拒绝参数,TS 也可以完全(并且经常)推断出正确的返回类型,因为它们是 2 种不同的算法。这里很明显返回类型位置的T应该是typeof arg1 | typeof arg2

    【讨论】:

    • 为什么 getMinimum([1, 2, 3], {length: 12}) 起作用呢?如果我为示例中的最后一个函数调用提供泛型类型,则函数调用如下所示:getMinimum<number[]>([1, 2, 3], {length: 12})。所以在这种情况下,{length: 12} 不是类型:number[],但它仍然有效。 Typescript 将结果推断为以下类型:number[] | {长度:数字}。
    • 这很奇怪,它不应该工作tsplay.dev/w1EG8w我会编辑我的答案来解决你的问题。它确实感觉不完整。
    • 明确一点,getMinimum([1, 2, 3], {length: 12}) 分配类型 number[] | {length number} 到返回值,但 getMinimum<number[]>([1, 2, 3], {length: 12}) 引发类型错误,这很好。但是根据您之前的解释, getMinimum([1, 2, 3], {length: 12}) 应该是不可能的。
    【解决方案2】:

    您的错误是由参数的隐式推断类型引起的。要使您的函数同时接受和数组字符串,您可以使用 union 类型:

    function getMinimum<T extends { length: number }>(arg1: T, arg2: T | string): T {
      if (arg1.length >= (arg2 as T).length) {
        return arg2 as T;
      } else {
        return arg1;
      }
    }
    

    或者,您可以在 res0 中使用显式泛型:

    const res0 = getMinimum<number[] | string>([1, 2, 3], str);
    

    【讨论】:

    • 通过此实现,res0 将在推断类型时返回 arg1 的类型。这真的不是一个好的选择。不要这样做。
    猜你喜欢
    • 2017-03-22
    • 2014-05-05
    • 1970-01-01
    • 1970-01-01
    • 2020-11-21
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 2020-10-15
    相关资源
    最近更新 更多