【问题标题】:Union type for generic param通用参数的联合类型
【发布时间】:2020-05-26 19:05:17
【问题描述】:

代理返回条件随机类型。

const numbersArray = [1,2,3,4];
const stringsArray = ['1','2','3','4'];


function func<T>(array: T[]): T[][] {
  return [[array[0], array[1]], [array[2], array[3]]];
}

const proxy = () => Math.random() < 0.5 ? numbersArray : stringsArray;

const resulNumbers = func(numbersArray);

const resultStrings = func(stringsArray);

const resultUnion = func(proxy()); // error

错误

const proxy: () => number[] | string[]

Argument of type 'number[] | string[]' is not assignable to parameter of type 'number[]'.
  Type 'string[]' is not assignable to type 'number[]'.
    Type 'string' is not assignable to type 'number'.(2345)

link to playground

解决这个问题的正确方法,有什么想法吗?

【问题讨论】:

标签: typescript generics union


【解决方案1】:

这个函数:

const proxy = () => Math.random() < 0.5 ? numbersArray : stringsArray;

有一个返回类型:

number[] | string[]

这种类型表示您要么拥有一个包含所有数字的数组,要么拥有一个包含所有字符串的数组,但绝不是两者的混合。

那么该类型的成员的类型是什么?您可能认为它是number | string,但完全 不是这样。如果你取string | number 并将数组符号添加到末尾,你会得到:

(number | string)[]

这种类型表示它是一个字符串或数字数组,每个成员都可以是其中一个。您现在可以将两者混合使用。这不是一回事。这意味着无法推断成员类型,因为该成员类型不能用于构造原始数组类型。

现在让我们看看泛型函数:

function func<T>(array: T[]): T[][] {
  return [[array[0], array[1]], [array[2], array[3]]];
}

此函数尝试为数组T[] 查找成员类型T。但如上所示,string[] | number[] 没有好的成员类型。所以 typescript 无法推断出T 并抛出错误。

因此,要使您的函数正常工作,成员类型 T 需要是可推断的。您可以通过将代理的返回类型转换为兼容的数组类型来做到这一点,其中只有数组的成员是联合体。

const proxy: () => (string | number)[] =
  () => Math.random() < 0.5 ? numbersArray : stringsArray;

现在T 可以变成string | number,一切都如您所愿。

const resultUnion = func(proxy()); // type: (string | number)[][]

Playground

【讨论】:

    【解决方案2】:

    发生错误是因为类型参数 T 如何解析对 func 的调用。 proxy 的返回类型为

    number[] | string[]
    

    并且T 的类型是根据number[] | string[] 与以下签名的匹配程度来推断的:

    function func<T>(x: T[]): T[][]
    

    所以x 必须是Ts 的数组,就func 而言,它可以是任何类型。在number[] | string[] 的情况下,T 的类型被推断为number,因为number[] 是联合中与T[] 匹配x 的第一个类型。不幸的是,string[] 没有被考虑在内。

    对于此调用,您可能希望 Tnumber | string,您可以通过在调用 func 时显式指定类型来实现:

    const xs: (number | string)[][] = func<number | string>(proxy())
    

    【讨论】:

      【解决方案3】:

      the question this duplicates 的答案解释了为什么 TypeScript 并不总是为泛型类型参数推断联合;这是因为当调用者为单个泛型类型参数指定多个事物时,它通常表示错误,并且如果编译器总是在这种情况下推断出一个联合,它将允许很多可能错误的代码。请参阅microsoft/TypeScript#19656 了解有关此问题的“官方”讨论。

      无论如何,在您的情况下,我可能会将func() 签名更改为:

      function func<T extends any[]>(array: T): T[number][][] {
        return [[array[0], array[1]], [array[2], array[3]]];
      }
      

      在这种情况下,T 实际上是传入数组的类型,而不是其成员的类型,即T[number]。我想这应该对你有用。

      希望有所帮助;祝你好运!

      Playground link to code

      【讨论】:

        猜你喜欢
        • 2021-07-03
        • 1970-01-01
        • 2020-01-14
        • 2016-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多