Distributive Conditionals

A Naked `T extends U` Distributes Over Unions

Distributive Conditionals

When the checked type is a naked generic parameter, conditional types distribute over unions. This is huge and surprising.

3 min read Level 3/5 #typescript#conditional-types#distributive
What you'll learn
  • Recognize when distribution happens
  • Disable it with `[T] extends [U]`
  • Use it to filter unions

When a conditional type checks a naked generic parameter against a type, and that parameter is a union, the conditional distributes over each member of the union.

The Behavior

type ToArray<T> = T extends any ? T[] : never;

type A = ToArray<string | number>;
// string[] | number[]
// (NOT (string | number)[])

The conditional ran once per union member, then unioned the results.

Filtering A Union

type StringsOnly<T> = T extends string ? T : never;

type A = StringsOnly<"a" | "b" | 1 | 2>;
// "a" | "b"

Distribution + never-as-filter is how Extract<T, U> and Exclude<T, U> work.

Disabling Distribution

Wrap the operands in tuples to STOP distribution:

type ToArray<T> = [T] extends [any] ? T[] : never;

type A = ToArray<string | number>;
// (string | number)[]
// (no distribution this time)

[T] extends [any] checks the tuple type — which isn’t a naked generic — so distribution doesn’t apply.

When Does Distribution Help

  • Filtering unions (Extract, Exclude)
  • Mapping over union members (ToArray<T>)
  • Building a union of results from a union of inputs

When Does It Hurt

When you want the WHOLE input type, not per-member behavior. Wrap in tuples to disable.

A Common Trap

type AllStrings<T> = T extends string ? "yes" : "no";

type A = AllStrings<"a" | "b">;     // "yes" (each member is a string)
type B = AllStrings<"a" | 1>;        // "yes" | "no" (mixed!)

If you wanted “all of T’s members must be strings”, distribution gives you the wrong answer:

type AllStrings<T> = [T] extends [string] ? "yes" : "no";

type A = AllStrings<"a" | "b">;     // "yes"
type B = AllStrings<"a" | 1>;        // "no"  ✓

Wrap in tuples to check the whole thing.

Up Next

The utility types — many built from these primitives.

Utility Types →