I'm trying to map a tuple to another tuple based on a name map and get a strongly typed tuple as the result.
In some cases a single element from the source tuple may map into multiple elements in the result. Also, the source tuple may have variable length.
this is what I have for now:
const t1 = ["a", "b", "c"] as const;
const nameMap = {
"a": [],
"b": ["j"],
"c": ["k", "l"],
} as const;
const t2 = t1.reduce<string[]>((acc, e) => {
acc.push(...nameMap[e]);
return acc;
}, []);
However, "t2" is an array here, not a strongly typed tuple. How do I get t2 to be a strongly typed tuple (["j", "k", "l"] in this case)?
(order does not matter)
For this to work you'll need to write a recursive conditional type that walks through the tuple type of the input array and maps them to another array using the name mapper, and builds up an output by concatenating these arrays.
For example, here's a tail-recursive conditional type called
MapNames<T, M>which takes an input tupleTand a mapping typeMthat converts the elements ofTinto a new array:It uses variadic tuple types to manipulate both the input and output tuples. Note that the third type parameter
Ais the accumulator and its use makes it tail-recursive and therefore amenable to fairly long input tuples. We break the input tupleTinto the first elementFand the rest of the elementsR. ThenFis a key of the mapping typeF, and we concatenate the arrayM[F]to the end of the accumulator, and recurse.Then you can write a generic
mapNames()function that takes inputs of typesTand anMand returns aMapNames<T, M>:This function is implemented the same way as your example, although there's no way for the compiler to understand that the implementation works for such a complicated recursive conditional generic type. So we have to loosen type safety inside the function via type assertions and the
anytype, or something like it.Let's test it out:
Looks good. The type of
t2matches the actual value at runtime.Playground link to code