Home > Blockchain >  Spread operator for a variable of arrays of different types returns an array of a union of those typ
Spread operator for a variable of arrays of different types returns an array of a union of those typ

Time:02-22

In Typescript, when spreading a variable of type number[] | string[] | boolean[], the resulting variable is of type (number | string | boolean)[]:

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

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

TS Playground

This was unexpected to me. I would have expected the type of b to be number[] | string[] | boolean[]. Does someone know why this would be the case?

CodePudding user response:

It seems to be intended that the outcome of an array spread will become a single array type even if the original array's type is a union of array types. See microsoft/TypeScript#28813 for more details.

I think that as a human being, you look at [...a] and see it as a single "copy" operation on a; but the compiler treats it more generally to support things like [1, ...a, 2] or [...a, ...a], etc. So the shortcut that [...a] should have type typeof a is not taken.

The issue at microsoft/TypeScript#28813 mentions that there are other places in TypeScript where unions do not propagate upward out of properties or downward into them exactly where someone might expect. For example, taking something like declare const x: {a: 0, b: 0} | {a: 1, b: 1} and copying it into a new object literal like const y = {a: x.a, b: x.b} will result in the new object type being {a: 0 | 1, b: 0 | 1} even though ostensibly it should be the same type as the original. The compiler simply does not distribute its analysis across all unions, since this would be prohibitively expensive in general. (See microsoft/TypeScript#25051 for a declined suggestion to allow developers to opt in to such analysis on an as-needed basis.) For array spreads this means that there's no general mechanism to support [...a, ...a] being interpreted as number[] | string[] | boolean[].

So that's what's happening and why.


As for alternatives, there was some work done on array spread with variadic tuples so if you use a const assertion to prevent widening of your array types, you will get something closer to what you're looking for:

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

Or, as you have discovered, and as mentioned in microsoft/TypeScript#28813, you can use slice():

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

Playground link to code

CodePudding user response:

Not an answer per se, but if anyone wants to know how to shallowly copy the array a without a type assertion such that it retains its type, you can use slice() without any parameters:

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

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

TS Playground

  • Related