Bird
Raised Fist0
Typescriptprogramming~15 mins

Distributive conditional types in Typescript - Deep Dive

Choose your learning style10 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.

Practice

(1/5)
1. What does a distributive conditional type do in TypeScript?
T extends U ? X : Y when T is a union type?
easy
A. Applies the condition separately to each member of the union type
B. Applies the condition once to the entire union type as a whole
C. Always returns the true branch X regardless of T
D. Always returns the false branch Y regardless of T

Solution

  1. Step 1: Understand conditional types with unions

    When T is a union, TypeScript distributes the condition over each member separately.
  2. Step 2: Apply distributive behavior

    Each member of T is checked against U individually, returning X or Y per member.
  3. Final Answer:

    Applies the condition separately to each member of the union type -> Option A
  4. Quick Check:

    Distributive conditional types = separate checks per union member [OK]
Hint: Remember: conditional types split unions into parts [OK]
Common Mistakes:
  • Thinking the condition applies once to the whole union
  • Assuming it always returns true or false branch
  • Confusing distributive with non-distributive conditional types
2. Which of the following is the correct syntax for a distributive conditional type in TypeScript?
easy
A. type Result<T> = if (T extends string) number else boolean;
B. type Result<T> = T => string ? number : boolean;
C. type Result<T> = T ? number : boolean;
D. type Result<T> = T extends string ? number : boolean;

Solution

  1. Step 1: Recall correct conditional type syntax

    Conditional types use T extends U ? X : Y syntax in TypeScript.
  2. Step 2: Match syntax with options

    type Result<T> = T extends string ? number : boolean; matches the correct syntax; others use invalid or incorrect syntax.
  3. Final Answer:

    type Result<T> = T extends string ? number : boolean; -> Option D
  4. Quick Check:

    Conditional types use extends ? : syntax [OK]
Hint: Look for extends ? : pattern in type definitions [OK]
Common Mistakes:
  • Using if-else instead of conditional operator
  • Missing the extends keyword
  • Using arrow functions incorrectly in type definitions
3. Given the type:
type FilterString<T> = T extends string ? T : never;
type Result = FilterString<'a' | 1 | 'b' | true>;

What is the type of Result?
medium
A. never
B. 'a' | 1 | 'b' | true
C. 'a' | 'b'
D. string

Solution

  1. Step 1: Apply distributive conditional type to each union member

    Each member is checked: 'a' extends string (true), 1 extends string (false), 'b' extends string (true), true extends string (false).
  2. Step 2: Collect true branch results

    Only 'a' and 'b' remain; others become never and are removed from union.
  3. Final Answer:

    'a' | 'b' -> Option C
  4. Quick Check:

    FilterString filters strings only = 'a' | 'b' [OK]
Hint: Distributive types filter unions by returning never for false cases [OK]
Common Mistakes:
  • Thinking the whole union is returned unchanged
  • Confusing never with empty string
  • Assuming the result is just string instead of filtered literals
4. Identify the error in this distributive conditional type:
type ExtractNumber<T> = [T] extends [number] ? T : never;
type Result = ExtractNumber<'a' | 1 | 2>;
medium
A. The condition is wrapped in a tuple, so distribution does not occur
B. The syntax is invalid because of missing extends keyword
C. The type will always return never
D. The union type is not allowed in conditional types

Solution

  1. Step 1: Understand tuple wrapping effect

    Wrapping T in a tuple disables distributive behavior in conditional types.
  2. Step 2: Analyze impact on ExtractNumber

    Since distribution is off, the whole union is checked at once, so 'a' | 1 | 2 extends number is false, returning never.
  3. Final Answer:

    The condition is wrapped in a tuple, so distribution does not occur -> Option A
  4. Quick Check:

    Tuple wrapping disables distributive conditional types [OK]
Hint: Tuple brackets stop distribution in conditional types [OK]
Common Mistakes:
  • Assuming distribution still happens with tuple wrapping
  • Confusing syntax error with logic error
  • Thinking union types are not allowed in conditional types
5. How can you create a type that converts a union of types T into a union of their array types using distributive conditional types?
Choose the correct type definition.
hard
A. type ToArray<T> = T extends any[] ? T : never;
B. type ToArray<T> = T extends any ? T[] : never;
C. type ToArray<T> = T[];
D. type ToArray<T> = [T] extends [any] ? T[] : never;

Solution

  1. Step 1: Understand distributive conditional type for mapping

    Using T extends any ? T[] : never applies the array transformation to each union member separately.
  2. Step 2: Compare options

    type ToArray<T> = T extends any ? T[] : never; correctly distributes and converts each member to an array type. type ToArray<T> = [T] extends [any] ? T[] : never; disables distribution by tuple wrapping. type ToArray<T> = T[]; wraps the whole union as an array, not each member. type ToArray<T> = T extends any[] ? T : never; filters arrays, not converts.
  3. Final Answer:

    type ToArray<T> = T extends any ? T[] : never; -> Option B
  4. Quick Check:

    Distributive conditional types map unions member-wise [OK]
Hint: Use T extends any ? to distribute over unions [OK]
Common Mistakes:
  • Wrapping T in tuple disables distribution
  • Using T[] directly converts whole union, not members
  • Confusing filtering with mapping in conditional types
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard