将元组与其余元素连接

时间:2019-01-04 21:23:26

标签: typescript typescript3.0

TypeScript 3.0将rest elements添加到元组类型。 但是,在这样的元组上调用concat()似乎会丢失类型:

type MyTupleType = [number, ...string[]];
let t1: MyTupleType = [42, 'foo'];
let t2 = t1.concat('bar');  // (string | number)[]

我可以使用as MyTupleType重新声明类型,但我希望有一些简单的方法。

1 个答案:

答案 0 :(得分:1)

TypeScript没有比这更简单的内置功能。 standard library typings for Array.prototype.concat()不在乎所讨论的数组是否为元组。从v3.2开始,通过数组操作来操纵元组类型的一般问题在TypeScript中不容易解决。您需要正确执行一些missing type operations

在像您这样的“休息元组”的特殊情况下,concat()的类型签名非常简单:输出类型与当前对象的类型相同(因为添加零或更多的“ tail”元素不会更改尾部)。因此,有可能支持您在此处提到的狭窄用例。

您可以通过merging in自己的声明来添加concat()的{​​{3}},只有在数组是一个剩余元组并且您要从尾巴。可能是这样的:

declare global {
  type IsRestTuple<T, Y=unknown, N=never> =
    T extends Array<any> ? number extends T['length'] ?
    T[number][] extends T ? N : Y : N : N

  interface Array<T> {
    concat<Tuple extends Array<any>>(
      this: Tuple & IsRestTuple<Tuple>,
      ...items: Array<Tuple[99999999] | Array<Tuple[99999999]>>
    ): Tuple;
  }
}

如果IsRestTuple<T>是一个休息类型元组,则unknown类型的函数将返回T,否则返回never。它使用一堆overload来确定。

然后,添加的conditional types重载使用genericthis的类型约束为通过IsRestTuple测试的事物(交集Tuple & IsRestTuple<Tuple>推断Tuplethis。如果IsRestTuple<Tuple>unknown,则交集变为Tuple,并且类型参数推断成功;如果为never,然后交集变成never,并且类型参数推断失败。)然后,它仅接受其余类型的尾部的参数(我假设元组的第99999999个元素将成为尾部的一部分)。如果选择此过载,则输出将是您期望的。让我们尝试一些示例:

type MyTupleType = [number, ...string[]];
let t1: MyTupleType = [42, 'foo'];
let t2 = t1.concat('bar'); // [number, ...string[]], success
let t3 = t1.concat(100); // Array<string | number>, 100 doesn't match string
let t4 = t1.concat(false); // compile error, false doesn't match string or number
let t5: [number] = [1];
let t6 = t5.concat(1); // number[], would be [number, number] but use case unsupported

所以t2是您想要的样子,其余情况不受影响。这是我可以按照要求最接近回答您的问题的方法。除非您发现此特定用例是您需要大量支持的东西,否则似乎并不值得:它复杂且丑陋,仅适用于其余元组和concat()。通过比较,您正在使用的类型断言看起来很吸引人。

无论如何,希望能有所帮助;祝你好运!