I was in need of proper tuple type inference in Typescript and stumbled upon a magic solution for my problem in the official Typescript GitHub.
In the following snippet, tuple3
is obviously the proposed solution. I have added some variations not leading to the expected result.
My question is: Why does tuple3
work like it does? I imagine union types working like an exclusive selection. Check, if the first type in the union fits, if so, use it, otherwise proceed to the next one. Following this rule, I would expect the compiler to find that (for tuple3
) [void]
doesn't fit the given argument for inference, so it uses {}
as type, leading to the same result as tuple4
. But apparently the types in the union ([void] | {}
) are connected somehow? Is this a feature or a bug? Is it documented somewhere?
function tuple1<TupleT>(tuple: TupleT): TupleT
{ return tuple; }
tuple1([42, 'foo', true]); // type: (string | number | boolean)[]
function tuple2<TupleT extends [void]>(tuple: TupleT): TupleT
{ return tuple; }
tuple2([42, 'foo', true]); // error: [number, string, boolean] not assignable to [void]
function tuple3<TupleT extends [void] | {}>(tuple: TupleT): TupleT
{ return tuple; }
tuple3([42, 'foo', true]); // type: [number, string, boolean]
function tuple4<TupleT extends {}>(tuple: TupleT): TupleT
{ return tuple; }
tuple4([42, 'foo', true]); // type: (string | number | boolean)[]
function tuple5<TupleT extends [any]>(tuple: TupleT): TupleT
{ return tuple; }
tuple5([42, 'foo', true]); // error: [number, string, boolean] not assignable to [any]