Sometimes you want to check a type without splitting it into parts. Non-distributive conditional types help you do that.
Non-distributive conditional types in Typescript
type NonDistributive<T> = [T] extends [SomeType] ? TrueType : FalseType;Wrapping the type T in square brackets [T] stops TypeScript from splitting unions.
This way, the condition checks the whole type, not each union member separately.
type IsString<T> = T extends string ? true : false; // For union type // IsString<'a' | number> becomes true | false (distributive)
T in brackets stops splitting. It checks the whole union at once.type IsStringNonDist<T> = [T] extends [string] ? true : false; // For union type // IsStringNonDist<'a' | number> becomes false (non-distributive)
This program shows the difference between distributive and non-distributive conditional types. The first splits the union and returns a union of results. The second checks the whole union at once and returns a single result.
type IsString<T> = T extends string ? true : false; type IsStringNonDist<T> = [T] extends [string] ? true : false; // Test with union // Distributive conditional type type Test1 = IsString<'hello' | 42>; // true | false // Non-distributive conditional type type Test2 = IsStringNonDist<'hello' | 42>; // false // To see results, we create variables with these types const test1a: Test1 = true; const test1b: Test1 = false; // const test2a: Test2 = true; // Error: Type 'true' is not assignable to type 'false' const test2b: Test2 = false; console.log('Test1 can be true or false:', test1a, test1b); console.log('Test2 is always false:', test2b);
Non-distributive conditional types are useful when you want to avoid unexpected splitting of union types.
Remember to wrap the type parameter in square brackets to make the condition non-distributive.
Conditional types normally split unions into parts and check each part.
Wrapping the type in square brackets stops this splitting, making the check non-distributive.
This helps when you want to test the whole type as one piece.
Practice
Solution
Step 1: Understand distributive conditional types
Distributive conditional types apply the condition to each member of a union type separately.Step 2: Understand non-distributive conditional types
Non-distributive conditional types evaluate the condition on the entire union type as a single entity, not distributing over members.Final Answer:
It evaluates the condition on the entire union type as a whole, not on each member separately. -> Option DQuick Check:
Non-distributive = whole union check [OK]
- Thinking conditional always distributes over unions
- Confusing non-distributive with type narrowing
- Assuming it only works on primitives
Solution
Step 1: Recognize non-distributive syntax
Wrapping the type parameter in a tuple, like [T], prevents distribution over unions.Step 2: Compare options
type NonDist<T> = [T] extends [string] ? true : false; uses [T] extends [string], which is the correct non-distributive syntax. type NonDist<T> = T extends string ? true : false; distributes over unions. Options C and D are invalid conditional syntax.Final Answer:
type NonDist<T> = [T] extends [string] ? true : false; -> Option CQuick Check:
Tuple wrapping disables distribution [OK]
- Not wrapping type in tuple, causing distribution
- Using invalid conditional syntax
- Confusing union with intersection in condition
type Test<T> = T extends string ? 'yes' : 'no'; type Result = Test<'a' | number>;
What is the type of
Result?Solution
Step 1: Understand distributive conditional types
SinceTis a union'a' | number, the condition distributes over each member.Step 2: Evaluate each member
'a' extends string is true, so 'yes'; number extends string is false, so 'no'. Combined result is 'yes' | 'no'.Final Answer:
'yes' | 'no' -> Option AQuick Check:
Distributes over union = 'yes' | 'no' [OK]
- Assuming whole union checked at once
- Expecting only 'yes' or only 'no'
- Confusing union with intersection
type NonDist<T> = T extends [string] ? true : false;
Solution
Step 1: Analyze the condition
The condition checks ifTextends[string], a tuple type, not wrappingT.Step 2: Understand distribution prevention
To prevent distribution,Titself must be wrapped in a tuple:[T] extends [string]. This code does not do that, so distribution is not prevented.Final Answer:
It incorrectly checks if T extends a tuple containing string, not preventing distribution. -> Option AQuick Check:
Wrap T, not string, to prevent distribution [OK]
- Wrapping the wrong side of extends
- Confusing tuple with type wrapper
- Assuming syntax error instead of logic error
T is exactly string | number (no more, no less)?Solution
Step 1: Prevent distribution with tuple wrapping
WrappingTin a tuple[T]prevents distribution over unions.Step 2: Use mutual assignability checks
Check[T] extends [string | number]and[string | number] extends [T]to ensure exact match, no extra or missing types.Final Answer:
type Exact<T> = [T] extends [string | number] ? ([string | number] extends [T] ? true : false) : false; -> Option BQuick Check:
Tuple wrap + mutual extends = exact type check [OK]
- Not preventing distribution, causing partial matches
- Checking only one extends direction
- Using incorrect tuple wrapping
Ready to prove it?
Pass the test and this pattern moves unknown
