Flatten tuple type of tuples in TypeScript
1type Flatten<T> = any; // implementation23type Step1 = Flatten<[1, [2, [3, [4]]]]>;4type Step2 = [1, ...Flatten<[2, [3, [4]]]>];5type Step3 = [1, 2, ...Flatten<[3, [4]]>];6type Step4 = [1, 2, 3, ...Flatten<[4]>];7type Result = [1, 2, 3, 4];
Today we discuss Flatten
It works the same way as Array.prototype.flat when you pass Infinity
Let's find out how to do that in TypeScript 💪
Iteration over tuple elements
Knowing the approach from different challenges, as we want to save the structure (it will be tuple at the end), we apply Type inference in conditional types with Rest elements in Tuples.
We iterate over elements:
1type Flatten<T> = T extends []2 ? []3 : T extends [infer Head, ...infer Tail]4 ? [] // make it flatten using Head and Tail5 : [];
Then we have 2 cases:
- If the element is a tuple, we apply changes recursively
- Otherwise, we leave it as is
Let's add second case:
1type Flatten<T> = T extends []2 ? []3 : T extends [infer Head, ...infer Tail]4 ? [Head, ...Flatten<Tail>]5 : [];
Call it recursively when needed
At the moment, if we have a look at Playground – https://tsplay.dev/w18bXW, we will find that not all tests are passed.
We forgot to apply function recursively when we have an element as tuple. Let's have an example here:
1type Step1 = Flatten<[1, [2]]>;2type Step2 = [1, ...Flatten<[[2]]>];3// ❌ Expected to have [1, 2] instead4type Result = [1, [2]];
In this case we cannot just add it to result tuple, we need to call Flatten
before and then put all the elements of it to the result type. Let's change the implementation based on that:
1type Flatten<T> = T extends []2 ? []3 : T extends [infer Head, ...infer Tail]4 ? Head extends any[]5 ? [...Flatten<Head>, ...Flatten<Tail>]6 : [Head, ...Flatten<Tail>]7 : [];
Now it's working as expected 🔥
Check out Playground – https://tsplay.dev/WKk0DW
And last but not least, as you see we return empty array []
in several places, let's simplify solution a little bit:
1type Flatten<T> = T extends [infer Head, ...infer Tail]2 ? Head extends any[]3 ? [...Flatten<Head>, ...Flatten<Tail>]4 : [Head, ...Flatten<Tail>]5 : [];
Shorter solution – https://tsplay.dev/WP7gzm
Thank you for your time and have a productive upcoming week 🚀
typescript