ProgrammingFrontend Developer

What are distributive types in TypeScript, where are they used, and what features should be taken into account when working with them?

Pass interviews with Hintsage AI assistant

Answer

Distributive types are a feature of TypeScript that manifest when working with conditional types (T extends U ? X : Y). When the type variable on the left side of extends is a union, TypeScript distributes the condition across each element of the union.

Example:

// "Test<A> | Test<B> | Test<C>" type Test<T> = T extends string ? () => string : () => number; type Result = Test<'A' | 'B' | 'C'>;

Here, Result will resolve to:

  • Test<'A'> | Test<'B'> | Test<'C'>

When to use it — for writing universal APIs, manipulating unions, for example, for filtering or pattern matching.

Features:

  • If you wrap T in a tuple [T], the distribution will not occur. This is a way to "disable" distribution.
  • Distribution works only for type variables (not for literals).

Tricky Question

Question: Will the type T[] extends number[] ? true : false be distributive?

Correct answer: No, distribution occurs only if the left-hand side is a type variable without being wrapped in an array or tuple. For example, the conditional type T extends number ? ... will distribute, but T[] extends number[] ? ... will not.


Examples of real errors due to ignorance of the subtleties of the topic


Story

Project: Props validation library for React components. They wanted to create a type that turns union props into a strict interface, but due to a lack of knowledge about distribution, the type became unexpectedly complex (union member properties got mixed up). They fixed it by adding a wrapper [T] to prevent distribution.


Story

Project: They aimed to handle all event types in one handler function. In the conditional type, it was expected that the function would accept each event type through distribution, but by not explicitly using it, the implementation led to incorrect argument types (union instead of a separate call for each type).


Story

Project: When creating custom utility types (like Exclude, Extract), they forgot that distribution does not happen for tuples and arrays. As a result, the Exclude type did not work for arrays (for example, the type Exclude<["a"|"b"], "b"> did not remove "b").