Typically, union type order doesn’t matter. But for a typing like
type A = { isFoo: true, foo: number }
type B = { isBar: true, bar: number }
type C = [A] | [B] | [A & B]
type D = [A & B] | [B] | [A]
the order matters because the error message on invalid type will be quite different for type C and type D
for instance,
const c: C = { isFoo: true, foo: 1, isBar:true }
will have error message of Object literal may only specify known properties, and 'isBar' does not exist in type 'A'.
but
const d: D = { isFoo: true, foo: 1, isBar:true }
but this will have ts error of Property 'bar' is missing in type '{ isFoo: true; foo: number; isBar: true; }' but required in type 'B'
which is more user friendly.
playground
I have tried some options like
type Reverse<T, R = never, O = T> = [T] extends [never] ? R : T extends infer U ? Reverse<Exclude<O, U>, U | R> : never;
type E = Reverse<C>
but the type of E
is still in the same order as C
.
I am at a loss if this is possible at all.
1
you can use some advanced TypeScript type manipulation technique
type Reverse<T, R = never> =
T extends [infer Head, ...infer Tail]
? Reverse<Tail, [Head, ...R]>
: R;
// Manually reordered type for the desired error message
type C = { isFoo: true, foo: number }
type D = { isBar: true, bar: number }
type E = { isFoo: true, foo: number, isBar: true }
type Union = [C] | [D] | [E]
type ReversedUnion = Reverse<Union>