Without
Challenge
Implement the type version of lodash .without()
. Without<T, U>
takes an
array T
, number or array U
and returns an array without the elements of U
.
type Res = Without<[1, 2], 1>; // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]>; // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]>; // expected to be []
Solution
This challenge was an interesting one, indeed. We need to implement the type that can filter out items from a tuple. We start with the initial type:
type Without<T, U> = any;
Since we need to work with the specific items in the tuple, I’m using the inferring to get the specific item and the rest of the tuple:
type Without<T, U> = T extends [infer H, ...infer T] ? never : never;
Having an item from the tuple, we can check if the item is the type U
. We need
this check in order to decide, should we add the element to the result or not:
type Without<T, U> = T extends [infer H, ...infer T]
? H extends U
? never
: never
: never;
In case it is “extends” from the input type U
, it means that we don’t need it
in our resulting type. So we just skip it and return a tuple without it. But,
since we need to process other items as well, we return not an empty tuple, but
a tuple with a recursive call to Without
again:
type Without<T, U> = T extends [infer H, ...infer T]
? H extends U
? [...Without<T, U>]
: never
: never;
That way, we skip anything that is specified as U
in our T
. However, once we
get a check that says we shouldn’t skip the element, we return a tuple with the
element itself:
type Without<T, U> = T extends [infer H, ...infer T]
? H extends U
? [...Without<T, U>]
: [H, ...Without<T, U>]
: never;
There is the last never
type left we need to address. Since we are working
with the variadic tuple types and spreading them, instead of never
we must
return an empty tuple:
type Without<T, U> = T extends [infer H, ...infer T]
? H extends U
? [...Without<T, U>]
: [H, ...Without<T, U>]
: [];
We got a working solution for a case, when U
specified as a primitive type.
But, in the challenge, there is also a case when it can be specified as a tuple
of numbers. To support this case, we can extend our type U
in H extends U
to
be a conditional type that checks that case.
If U
is a tuple of numbers, we return all the items in there as a union,
otherwise - just U
:
type Without<T, U> = T extends [infer H, ...infer T]
? H extends (U extends number[] ? U[number] : U)
? [...Without<T, U>]
: [H, ...Without<T, U>]
: [];
Congratulations! We have implemented a lodash version of .without()
method in
the type system.
Comments