TypeScript 3.0将rest elements添加到元组类型。 但是,在这样的元组上调用concat()似乎会丢失类型:
type MyTupleType = [number, ...string[]];
let t1: MyTupleType = [42, 'foo'];
let t2 = t1.concat('bar'); // (string | number)[]
我可以使用as MyTupleType
重新声明类型,但我希望有一些简单的方法。
答案 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重载使用generic将this
的类型约束为通过IsRestTuple
测试的事物(交集Tuple & IsRestTuple<Tuple>
推断Tuple
为this
。如果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()
。通过比较,您正在使用的类型断言看起来很吸引人。
无论如何,希望能有所帮助;祝你好运!