Issue
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)[];
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?
Solution
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[]
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.