Concept 8 of 12
Distributivity
Distribution is the rule that surprises everyone once and then explains half the confusing behaviour in the type system. A conditional whose checked side is a bare type parameter does not run once — it runs once per union member, and the results are unioned back together.
What "naked" means
type Naked<T> = T extends string ? 'yes' : 'no' type A = Naked<string | number> // 'yes' | 'no' — ran twice type Wrapped<T> = [T] extends [string] ? 'yes' : 'no' type B = Wrapped<string | number> // 'no' — ran once, on the whole union
Naked means the parameter stands alone on the left of `extends`, not wrapped in a tuple, array, object or promise. Wrapping it in anything switches distribution off and compares the union as a single thing.
Distribution is how filtering works
type MyExclude<T, U> = T extends U ? never : T MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c' // Each member becomes never or survives; unioning drops the nevers, // because never is the identity element of union.
`Exclude`, `Extract` and `NonNullable` are all one distributive conditional. The trick is that `never` vanishes from a union, so mapping unwanted members to `never` removes them for free.
Two idioms worth memorising
// Force distribution, even when you do not need the test: type Each<K> = K extends K ? [K] : never // Suppress it, and detect the empty union: type IsNever<T> = [T] extends [never] ? true : false
`K extends K` is trivially true and exists purely to distribute. `[T] extends [never]` is the only reliable way to ask "is this never", because a distributive conditional over `never` produces `never` and never reaches either branch.
The common wrong answer
// Intent: does T contain string? type HasString<T> = T extends string ? true : false HasString<string | number> // boolean, not true // It distributed: true | false, which collapses to boolean. // Fixed: ask about the union as a whole. type HasString2<T> = [T] extends [string] ? true : false
A conditional returning `boolean` instead of `true` or `false` is nearly always distribution you did not intend. The union of `true` and `false` is `boolean`, which is why the symptom looks so innocuous.
Takeaway
A bare parameter distributes; wrapping it in `[]` stops it. When a type mysteriously returns `boolean` or `never`, distribution is the first thing to check.