Typescript tuple inference in generic type

29 Views Asked by At

Let say I want to write a function that takes as a parameter a two elements tuple.

The first element of the tuple is a tuple of strings, the second a tuple containing the same strings, but capitalized. The function then just returns this tuple.

I want my function to typecheck the parameters and the output.

So:

capitalizeTuple([["foo"], ["Bar"]]); 

Should raise an error, because ["Bar"] is not the capitalized version of ["foo"]

And :

capitalizedTuple([["foo", "bar"], ["Foo","Bar"]]);

Should be okay, and the output infered as [["foo", "bar"], ["Foo", "Bar"]]

I first define :

type CapitalizeTuple<T extends string[]> = [
  T,
  { [I in keyof T]: Capitalize<T[I]> }
];

Then, the naive approach is to do :

declare const capitalizeTuple: <T extends CapitalizeTuple<string[]>>(t: T) => T;

capitalizeTuple([["foo"], ["Bar"]]);

Typescript does not complain and the infered type is : [string[], "Bar"[]] (the first tuple is NOT inferred as a tuple but as a string array)

My workaround to solve this problem is:

1/ Using the generic parameter T just for the inference of the first tuple

2/ Injecting [...T] (T alone won't be inferred as a tuple) into the generic type (CapitalizeTuple here)

3/ Using a second generic parameter U, to infer the actual type of the parameter t, and using it to infer the return type

declare const fixedCapitalizeTuple: <T extends string[], U>(
  t: CapitalizeTuple<[...T]> & U
) => U;

fixedCapitalizeTuple([
  ["foo", "bar"],
  ["Foo", "Bar"],
]); // infered as [["foo", "bar"], ["Foo", "Bar"]] as expected

Is there any way to achieve my goal using only one type parameter, for instance T ?

0

There are 0 best solutions below