2

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]
Neonit
  • 680
  • 8
  • 27
  • 1
    I think the key is in the comment, "add a tuple-like constraint ". If the compiler sees the tuple constraint it will go into inferring tuples for array literals. Now obviously the tuples will not satisfy `[void]`, but since `T` can be `[void] | {}` the tuples will satisfy `{}` but the `[void]` part put the compiler in the 'mood' to infer tuples. An interesting note is that this function will allow ANY object as an argument not just a tuple ! `tuple3({ test : 'ss'});` works just fine. I would use a more conventional solution with multiple overloads.. – Titian Cernicova-Dragomir May 16 '18 at 09:41
  • 1
    While not pretty this would be the overload solution: https://stackoverflow.com/questions/49729550/implicitly-create-a-tuple-in-typescript/49730427#49730427 – Titian Cernicova-Dragomir May 16 '18 at 09:46

0 Answers0