The term distributive refers to how union types should be treated when subjected to type-level operations (such as keyof or mapped types).
- Non-distributive (default) operations are applied to properties that exist on every member of the union.
- Distributive operations are applied to all members of the union separately.
Let's use an example.
type Fruit =
| { species: 'banana', curvature: number }
| { species: 'apple', color: string }
Let's assume that, for some reason, you want to know all possible keys that can exist on a Fruit.
Non-distributive
Your intuition may tell you to do:
type KeyOfFruit = keyof Fruit; // "species"
However, this will give you only the properties that exist on every member of the union. In our example, species is the only common property shared by all Fruit.
It's the same as applying keyof to the union of the two types.
keyof ({ species: 'banana', curvature: number } | { species: 'apple', color: string })
Distributive
With distribution, the operation is not performed on just the common properties. Instead, it is done on every member of the union separately. The results are then added together.
type DistributedKeyOf<T> =
T extends any
? keyof T
: never
type KeyOfFruit = DistributedKeyOf<Fruit>; // "species" | "curvature" | "color"
In this case, TypeScript applied keyof to each member of the union, and summed the results.
keyof { species: 'banana', curvature: number } | keyof { species: 'apple', color: string }