0
0
Typescriptprogramming~15 mins

Distributive conditional types in Typescript - Deep Dive

Choose your learning style9 modes available
Overview - Distributive conditional types
What is it?
Distributive conditional types in TypeScript are a special behavior of conditional types that automatically apply the condition to each member of a union type separately. This means if you have a union like A | B, the conditional type checks A and B individually and then combines the results. It helps create flexible and powerful type transformations based on unions.
Why it matters
Without distributive conditional types, handling unions in type transformations would be cumbersome and less precise. They allow TypeScript to automatically break down complex union types and apply logic to each part, making type definitions more expressive and safer. This leads to better code correctness and developer experience when working with diverse data shapes.
Where it fits
Learners should first understand basic TypeScript types, union types, and conditional types. After mastering distributive conditional types, they can explore advanced type manipulation techniques like mapped types, infer keyword, and recursive conditional types.
Mental Model
Core Idea
Distributive conditional types automatically split union types and apply the condition to each member separately, then combine the results back into a union.
Think of it like...
Imagine you have a basket of different fruits (an apple, a banana, and an orange). Distributive conditional types are like checking each fruit one by one to decide if it’s ripe, instead of checking the whole basket at once.
Union Type: A | B | C

Distributive Conditional Type:
  Apply condition to A → ResultA
  Apply condition to B → ResultB
  Apply condition to C → ResultC

Final Type: ResultA | ResultB | ResultC
Build-Up - 7 Steps
1
FoundationUnderstanding union types basics
🤔
Concept: Union types allow a value to be one of several types.
In TypeScript, you can write a type like `type Fruit = 'apple' | 'banana' | 'orange';` which means a variable of type Fruit can be any one of those strings. This is useful to represent multiple possible values in a single type.
Result
You can assign 'apple' or 'banana' to a variable of type Fruit, but not 'carrot'.
Knowing union types is essential because distributive conditional types work by splitting these unions into parts.
2
FoundationBasics of conditional types
🤔
Concept: Conditional types choose one type or another based on a condition.
A conditional type looks like `T extends U ? X : Y`. It means: if type T can be assigned to type U, then the type is X; otherwise, it is Y. For example, `type IsString = T extends string ? true : false;` will be true if T is string, else false.
Result
For `IsString<'hello'>` the result is true; for `IsString<42>` the result is false.
Conditional types let you write logic inside types, which is the foundation for distributive behavior.
3
IntermediateHow conditional types distribute over unions
🤔Before reading on: do you think a conditional type applied to a union checks the whole union at once or each member separately? Commit to your answer.
Concept: Conditional types automatically apply to each member of a union separately, then combine the results.
If you have `type T = A | B` and a conditional type `T extends U ? X : Y`, TypeScript treats it as `(A extends U ? X : Y) | (B extends U ? X : Y)`. This is called distributive conditional types. For example, `type Result = ('a' | 'b') extends string ? true : false;` results in `true` because both 'a' and 'b' extend string.
Result
The conditional type is applied individually to each union member, producing a union of results.
Understanding this automatic splitting is key to predicting how complex conditional types behave with unions.
4
IntermediatePreventing distribution with wrapping types
🤔Before reading on: do you think wrapping a union in a tuple affects distributive conditional types? Commit to your answer.
Concept: Wrapping a union type in a tuple or another container stops the distribution behavior.
If you write `type T = [A | B] extends [U] ? X : Y`, the conditional type does NOT distribute. It treats the whole union as one type. For example, `type NoDist = [string | number] extends [string] ? true : false;` results in false because the whole union is checked at once.
Result
You can control when distribution happens by wrapping the type in a tuple or object.
Knowing how to stop distribution helps write precise conditional types when you want to treat unions as a whole.
5
AdvancedUsing distributive conditional types for filtering
🤔Before reading on: can distributive conditional types be used to remove types from a union? Commit to your answer.
Concept: Distributive conditional types can filter union members by conditionally excluding some types.
For example, `type ExcludeString = T extends string ? never : T;` removes all string types from T. If T is `string | number | boolean`, the result is `number | boolean` because string members become never and disappear from the union.
Result
You get a new union type with only the members that do not match the condition.
This filtering pattern is a powerful way to manipulate union types dynamically.
6
AdvancedCombining infer with distributive conditional types
🤔Before reading on: do you think infer works inside distributive conditional types to extract parts of union members? Commit to your answer.
Concept: You can use the infer keyword inside distributive conditional types to extract subtypes from each union member.
For example, `type ElementType = T extends (infer U)[] ? U : T;` extracts the element type if T is an array. If T is `string[] | number[]`, the distributive conditional extracts `string | number` as the element type.
Result
You get a union of extracted types from each member of the original union.
Combining infer with distribution unlocks advanced type extraction and transformation capabilities.
7
ExpertSurprising behavior with nested unions and distribution
🤔Before reading on: do nested unions inside conditional types distribute multiple times or just once? Commit to your answer.
Concept: Distribution happens only at the top-level union in the checked type, not recursively inside nested unions.
If you have a type like `type T = (A | B) | C` and apply a conditional type, distribution applies to `A | B | C` at the top level. But if a union is nested inside a property or tuple, it does not distribute inside that nested structure automatically. For example, `type Nested = { x: A | B }` does not distribute over A and B inside the object.
Result
Distribution is shallow and only applies to the outermost union type.
Knowing the limits of distribution prevents confusion when working with complex nested types.
Under the Hood
At compile time, TypeScript checks if the type on the left side of the conditional is a union. If it is, it splits the union into individual members and applies the conditional check to each member separately. Then it recombines the results into a new union type. This happens during type inference and checking, not at runtime.
Why designed this way?
This design allows more precise and flexible type transformations without requiring explicit loops or mappings over unions. It leverages the union's nature to represent multiple possibilities and applies logic to each possibility automatically. Alternatives like manual mapping would be verbose and error-prone.
┌─────────────────────────────┐
│ Conditional Type: T extends U ? X : Y │
└─────────────┬───────────────┘
              │
      Is T a union? ── Yes ──┐
              │             │
              │             ▼
              │    Split T into members: T1, T2, ...
              │             │
              │    Apply condition to each member:
              │    T1 extends U ? X : Y
              │    T2 extends U ? X : Y
              │    ...
              │             │
              └─────────────┤
                            ▼
                  Combine results into union
Myth Busters - 4 Common Misconceptions
Quick: Does wrapping a union type in parentheses stop distributive conditional types? Commit to yes or no.
Common Belief:Wrapping a union type in parentheses stops distributive conditional types from applying.
Tap to reveal reality
Reality:Parentheses do NOT stop distribution; only wrapping in a tuple or object type stops it.
Why it matters:Misunderstanding this leads to unexpected type results and bugs when trying to control distribution.
Quick: Do distributive conditional types apply recursively inside nested types? Commit to yes or no.
Common Belief:Distributive conditional types apply recursively inside all nested unions automatically.
Tap to reveal reality
Reality:Distribution only applies to the top-level union type, not nested unions inside objects or tuples.
Why it matters:Assuming recursive distribution causes confusion and incorrect type expectations in complex types.
Quick: Does a conditional type applied to a non-union type distribute? Commit to yes or no.
Common Belief:Conditional types always distribute, even if the type is not a union.
Tap to reveal reality
Reality:Distribution only happens if the checked type is a union; otherwise, the conditional applies once.
Why it matters:Expecting distribution on non-unions leads to misunderstanding type behavior and debugging frustration.
Quick: Can distributive conditional types be used to filter union members? Commit to yes or no.
Common Belief:Distributive conditional types cannot remove or filter types from a union.
Tap to reveal reality
Reality:They can filter union members by returning never for unwanted types, effectively removing them.
Why it matters:Missing this powerful use limits the ability to write concise and expressive type filters.
Expert Zone
1
Distribution only triggers when the checked type is a naked type parameter or a union type directly; wrapping it in other types disables distribution.
2
When multiple conditional types are stacked, distribution applies at each level, which can cause subtle interactions and requires careful ordering.
3
Using never in the true branch of a distributive conditional type removes that member from the resulting union, enabling powerful filtering patterns.
When NOT to use
Avoid distributive conditional types when you want to treat a union as a single whole type or when working with nested unions inside objects. Instead, use tuple wrapping or explicit mapped types to control behavior.
Production Patterns
Distributive conditional types are widely used in utility types like Exclude, Extract, ReturnType, and custom filters. They enable flexible API typings, conditional transformations, and precise type narrowing in large codebases.
Connections
Set theory
Distributive conditional types mirror set operations by applying conditions to each element of a union set.
Understanding union types as sets helps grasp how distribution breaks down and recombines elements, similar to filtering or mapping sets.
Functional programming map/filter
Distributive conditional types act like map and filter functions over union types at the type level.
Seeing conditional types as type-level map/filter clarifies how they transform unions element-wise.
Biology: Cell differentiation
Just like stem cells differentiate into specific cell types based on signals, distributive conditional types differentiate union members into specific types based on conditions.
This analogy shows how a single input can split into specialized outputs depending on context, deepening understanding of type distribution.
Common Pitfalls
#1Expecting parentheses to stop distribution
Wrong approach:type T = (string | number) extends string ? true : false;
Correct approach:type T = [string | number] extends [string] ? true : false;
Root cause:Parentheses do not create a new type wrapper; only tuples or objects do, which control distribution.
#2Assuming distribution applies inside nested objects
Wrong approach:type T = { x: string | number } extends { x: string } ? true : false;
Correct approach:type T = { x: string | number } extends { x: string | number } ? true : false;
Root cause:Distribution only applies to top-level unions, not unions nested inside object properties.
#3Using conditional types to filter without returning never
Wrong approach:type Filter = T extends string ? T : T;
Correct approach:type Filter = T extends string ? never : T;
Root cause:To remove types from a union, the conditional must return never for unwanted members.
Key Takeaways
Distributive conditional types automatically apply conditions to each member of a union type separately.
This behavior enables powerful type transformations like filtering and extracting parts of union members.
Wrapping a union in a tuple or object stops distribution, allowing control over when it happens.
Distribution only applies to the top-level union and does not recurse into nested unions.
Understanding distributive conditional types unlocks advanced TypeScript type programming and safer code.