AnyOf
Challenge
Implement Python liked any
function in the type system. A type takes the array
and returns true
if any element of the array is true. If the array is empty,
return false
. For example:
type Sample1 = AnyOf<[1, "", false, [], {}]>; // expected to be true
type Sample2 = AnyOf<[0, "", false, [], {}]>; // expected to be false
Solution
Once I saw the challenge, my first idea was to use distributive conditional types.
We can use the syntax T[number]
to take the union of all elements from the
tuple. Having a union of the elements, we iterate them and get a union of
false
or true
types. In case all the elements returned false
, we get the
false
type literal. But, having even a single true
-y element results in
true
type literal. Hence, our union will comprise false
and true
type
literals, the product of which is boolean
. Checking the product of a union
against false
type literal says if there was a true
element.
But, the actual implementation for this turned out to be really quirky. I don’t like it, take a look:
type AnyOf<T extends readonly any[], I = T[number]> = (
I extends any ? (I extends Falsy ? false : true) : never
) extends false
? false
: true;
So I’ve started thinking, can we make it more maintainable? Turns out we can. Let us remember about inferring from tuple types combined with variadic tuple types. Remember, we used these when solving Last challenge or Pop and the likes.
We start from inferring the single element from the tuple and the rest of the family:
type AnyOf<T extends readonly any[]> = T extends [infer H, ...infer T]
? never
: never;
How can we check if the inferred element H
is false-y? First, we can construct
a type that represents false-y types. Let’s call it Falsy
:
type Falsy = 0 | "" | false | [] | { [P in any]: never };
Having a type that represents false-y values, we can just use a conditional type
to check if the H
extends from the type:
type AnyOf<T extends readonly any[]> = T extends [infer H, ...infer T]
? H extends Falsy
? never
: never
: never;
What do we do if the element is false-y? It means we are still trying to check if there is at least one true-y element. So we can continue recursively:
type AnyOf<T extends readonly any[]> = T extends [infer H, ...infer T]
? H extends Falsy
? AnyOf<T>
: never
: never;
Finally, once we see the element is not false-y, it means it is true-y. It makes
little sense to continue recursively since we already know there is true-y
element. So we just exit from recursion by returning true
type literal:
type AnyOf<T extends readonly any[]> = T extends [infer H, ...infer T]
? H extends Falsy
? AnyOf<T>
: true
: never;
The last state is when we have an empty tuple. In such case our inferring will
not work, meaning there is definitely no true-y elements. We can return false
in such case:
type AnyOf<T extends readonly any[]> = T extends [infer H, ...infer T]
? H extends Falsy
? AnyOf<T>
: true
: false;
That’s how we made an implementation of AnyOf
function in the type system. For
the reference here is the whole implementation:
type Falsy = 0 | "" | false | [] | { [P in any]: never };
type AnyOf<T extends readonly any[]> = T extends [infer H, ...infer T]
? H extends Falsy
? AnyOf<T>
: true
: false;
Comments