// init the array according to number.
type NumberToArr<T,P extends unknown[] = []> = P['length'] extends T ? P : NumberToArr<T,[...P,1]>
// detail impl
type NumberRangeImpl<K,Arr extends unknown[]> = Arr['length'] extends K ? K : (Arr['length'] | NumberRangeImpl<K,[...Arr,1]>)
type G = NumberToArr<9>
type NumberRange<T extends number,K extends number> = NumberRangeImpl<K,NumberToArr<T>>
type result9 = NumberRange<2 , 5> // 5 | 2 | 3 | 4 | expected 2 | 3 | 4 | 5
I’m trying to implement a TypeScript type to generate a numeric range union (e.g., 2 | 3 | 4 | 5 ). My code seems logically correct, but the union type order is unexpected (e.g., shows as 5 | 2 | 3 | 4 ). Is this a code error or a TypeScript behavior?
The union appears out of order: 5 | 2 | 3 | 4 instead of 2 | 3 | 4 | 5.
But this is just how TypeScript displays union types. TypeScript does not preserve order in unions. It may rearrange them arbitrarily when displaying them, especially for performance or internal canonicalization reasons.
So: type result = 2 | 3 | 4 | 5;
is exactly the same as:
type result = 5 | 2 | 3 | 4;
From the compiler’s perspective, these are identical types.
###Conclusion
There is no error in your logic — the type NumberRange<2, 5>does produce the correct union, just displayed in a different order.
Tip: Test it!
To verify, you can do something like:
type R = NumberRange<2, 5>;
const test: R = 3; // valid
const test2: R = 6; // error