Why ts-toolbelt library use “O extends unknown” expression

Presumably the library authors decided that UnionOf<A | B | C> should produce the same result as UnionOf<A> | UnionOf<B> | UnionOf<C>, and the definition of _UnionOf did not do that. The O extends unknown ? ... : never check, which deceptively looks like it does nothing, causes that to happen.

Expressions that look like they do nothing but actually distribute across unions, when T is a generic type parameter:

  • T extends unknown ? ... : never
  • T extends any ? ... : never
  • T extends T ? ... : never

If T is a type parameter, as in the generic function function foo<T>(/*...*/): void or the generic interface interface Foo<T> {/*...*/}, then a type of the form T extends XXX ? YYY : ZZZ is a distributive conditional type.

It distributes the conditional check across unions in T. When T is specified with some specific type, the compiler splits that type up into its union members, evaluates the check for each such member, and then joins the results back into a new union. So if F<T> distributes across unions, then F<A | B | C> will be the same as F<A> | F<B> | F<C>.


Not all type functions are distributive across unions. For example, the keyof operator does not turn unions of inputs into unions of outputs:

type KeyofA = keyof {a: string}; // "a"
type KeyofB = keyof {b: number}; // "b"
type KeyofAorB = keyof ({ a: string } | { b: number }); // never

Similarly, _UnionOf is not distributive across unions (related to the fact that it uses keyof in its definition):

type Oops = _UnionOf<{ a: string } | { b: number }>
// never

If you have a type function which is not distributive in unions and you want it to be, you can wrap it in a distributive conditional type:

type DistribKeyof<T> = T extends unknown ? keyof T : never;

type UnionKeyofAorB = DistribKeyof<{ a: string } | { b: number }>; // "a" | "b"

And therefore UnionOf is distributive across unions:

type Correct = UnionOf<{ a: string } | { b: number }>
// string | number

Playground link to code

Leave a Comment