Reverse
Challenge
Implement the type version of Array.reverse()
. For example:
type a = Reverse<["a", "b"]>; // ['b', 'a']
type b = Reverse<["a", "b", "c"]>; // ['c', 'b', 'a']
Solution
Reversing the tuple is easier than it seems to be. You need to get the last element of a tuple and put it first in another tuple. Continuing this operation recursively gives us a reverse tuple in the end.
We start with the initial type to fill it with the implementation later:
type Reverse<T> = any;
Now, as we talked earlier, we need to get the last element of a tuple and the rest of it. To do so, apply the inferring within conditional types:
type Reverse<T> = T extends [...infer H, infer T] ? never : never;
Pay attention to the spread we have in the first part of the tuple. With this
construct we are saying “TypeScript, give us the whole tuple without the last
element and the last element assign to the type parameter T
”. Having the last
element of the tuple, we can create a new one and put the T
inside of it:
type Reverse<T> = T extends [...infer H, infer T] ? [T] : never;
Doing so, we get the last element put as the first one. But, we need to apply
the same operation to other elements in the tuple. It is easily achievable by
calling the Reverse
again recursively:
type Reverse<T> = T extends [...infer H, infer T] ? [T, Reverse<H>] : never;
But calling a Reverse
will give us a tuple inside of a tuple and the more we
call it, the more depth we get. That’s not what we want. Instead, we need to get
a plain tuple. Applying the spread operator to the result of Reverse
type, we
can make it plain:
type Reverse<T> = T extends [...infer H, infer T] ? [T, ...Reverse<H>] : never;
What if the type parameter T
does not match the pattern of head and a tail? In
this case, we return the empty tuple to not break the spread:
type Reverse<T> = T extends [...infer H, infer T] ? [T, ...Reverse<H>] : [];
Comments