不同类型数组变量的扩展运算符 returns 这些类型的联合数组

Spread operator for a variable of arrays of different types returns an array of a union of those types

在 Typescript 中,当传播类型为 number[] | string[] | boolean[] 的变量时,结果变量的类型为 (number | string | boolean)[]:

const a: number[] | string[] | boolean[] = [];

const b = [...a] //Type of b is now (number | string | boolean)[];

TS Playground

这出乎我的意料。我本以为 b 的类型是 number[] | string[] | boolean[]。有人知道为什么会这样吗?

本身不是答案,但是如果有人想知道如何在没有类型断言的情况下浅层复制数组 a 以使其保留其类型,您可以使用 slice() 而不带任何参数:

const a: number[] | string[] | boolean[] = [];

const b = a.slice() //Type of b is now number[] | string[] | boolean[];

TS Playground

即使原始数组的类型是 union of array types. See microsoft/TypeScript#28813 以获取更多详细信息,数组展开的结果似乎也将成为单一数组类型。

我认为作为一个人,你看着 [...a] 并将其视为对 a 的单一“复制”操作;但编译器更普遍地对待它以支持 [1, ...a, 2][...a, ...a] 等东西。因此 [...a] 应该具有类型 typeof a 的快捷方式未被采用。

microsoft/TypeScript#28813 中的问题提到,在 TypeScript 的其他地方,联合不会向上传播出属性或向下传播到属性中,这正是人们所期望的。例如,将 declare const x: {a: 0, b: 0} | {a: 1, b: 1} 之类的内容复制到 const y = {a: x.a, b: x.b} 之类的新对象文字中,将导致新对象类型为 {a: 0 | 1, b: 0 | 1},即使表面上它应该与原始类型相同.编译器根本不会将其分析分布到所有联合中,因为这通常会非常昂贵。 (有关允许开发人员在 as-needed 基础上选择加入此类分析的拒绝建议,请参阅 microsoft/TypeScript#25051。)对于数组传播,这意味着没有通用机制来支持 [...a, ...a] 被解释为number[] | string[] | boolean[].

这就是正在发生的事情及其原因。


至于备选方案,已对使用 variadic tuples so if you use a const assertion 展开的数组进行了一些工作,以防止扩大数组类型,您将得到更接近您所寻找的东西:

const c = [...a] as const;
// const c: readonly number[] | readonly string[] | readonly boolean[]

或者,正如您所发现的那样,作为 mentioned in microsoft/TypeScript#28813,您可以使用 slice():

const d = a.slice();
// const d: number[] | string[] | boolean[]

Playground link to code