Implement a type `IsUnion`, which takes an input type `T` and returns whether `T` resolves to a union type. Learn union type manipulation in this medium-level challenge on TypeScriptPro.
In this medium-level challenge, you'll implement an IsUnion<T> type that detects whether a given type is a union type, correctly handling edge cases like never, any, unknown, and unions that collapse into a single type (e.g., string | 'a' resolves to string).
Implement a type IsUnion, which takes an input type T and returns whether T resolves to a union type.
For example:
type case1 = IsUnion<string> // false
type case2 = IsUnion<string | number> // true
type case3 = IsUnion<[string | number]> // falseChange the following code to make the test cases pass (no type check errors).
type cases = [
Expect<Equal<IsUnion<string>, false>>,
Expect<Equal<IsUnion<string | number>, true>>,
Expect<Equal<IsUnion<'a' | 'b' | 'c' | 'd'>, true>>,
Expect<Equal<IsUnion<undefined | null | void | ''>, true>>,
Expect<Equal<IsUnion<{ a: string } | { a: number }>, true>>,
Expect<Equal<IsUnion<{ a: string | number }>, false>>,
Expect<Equal<IsUnion<[string | number]>, false>>,
// Cases where T resolves to a non-union type.
Expect<Equal<IsUnion<string | never>, false>>,
Expect<Equal<IsUnion<string | unknown>, false>>,
Expect<Equal<IsUnion<string | any>, false>>,
Expect<EUnlock 102+ medium, hard, and extreme challenges to master advanced TypeScript.
One-time payment. Lifetime access.
type IsUnion<T, Copy = T> =
[T] extends [never]
? false
: T extends T
? [Copy] extends [T]
? false
: true
: never;How it works:
Copy which defaults to T, preserving the original full union type before distribution occurs.[T] extends [never] handles the never case. Wrapping in a tuple prevents distribution, so never correctly returns false.T extends T is a distributive conditional -- when T is a union like string | number, this distributes over each member, executing the body once for string and once for number.T is now a single member of the union (e.g., string), while Copy still holds the full original union (e.g., string | number).[Copy] extends [T] checks whether the full union fits within the current single member. For a true union, [string | number] extends [string] is false, so we return true.string alone, T extends T does not distribute meaningfully, and [string] extends [string] is true, so we return false.string | 'a' resolve to just string before the type is evaluated, so IsUnion correctly returns false for these.This challenge helps you understand distributive conditional types and the mechanics of union type detection, and how to apply these patterns in real-world scenarios.
This challenge is originally from here.