What are Conditional Types in TypeScript?
Understand TypeScript conditional types: T extends U ? X : Y, the infer keyword, union distribution, and recursion — with clear, correct code examples.
Expected Interview Answer
Conditional types let a type choose between two options based on a type-level test, using the syntax T extends U ? X : Y. They let you express branching logic in the type system, resolving to X when T is assignable to U and Y otherwise.
The power comes from combining them with the infer keyword to extract parts of a type, and from distribution over union types: when the checked type is a naked type parameter, the conditional applies to each member of the union separately. This underpins utilities like ReturnType, Exclude, and Extract, and enables recursive type-level programming for things like unwrapping Promises or flattening arrays.
- Adds branching logic at the type level
- Extracts inner types with infer
- Distributes automatically over unions
- Powers utilities like ReturnType and Exclude
- Enables precise, self-adjusting APIs
AI Mentor Explanation
Think of a selection rule: if a player is a bowler, assign them an over quota, otherwise a batting slot. A conditional type is that rule at the type level — 'if T extends Bowler, give BowlingRole, else BattingRole' — deciding the resulting type by testing what the input actually is.
Step-by-Step Explanation
Step 1
Write the test
Use T extends U to ask whether T is assignable to U at the type level.
Step 2
Pick the branches
Follow the test with ? X : Y to choose the true and false result types.
Step 3
Extract with infer
Introduce infer R inside U to capture a portion of the type, e.g. the return type.
Step 4
Leverage distribution
With a naked type parameter over a union, the conditional applies to each member separately.
Step 5
Recurse if needed
Reference the conditional within its own branches to unwrap nested structures like Promise<Promise<T>>.
What Interviewer Expects
- Knows the T extends U ? X : Y syntax
- Can use infer to extract inner types
- Understands distributive behavior over unions
- Knows how to disable distribution with tuple wrapping
- Connects conditionals to ReturnType/Exclude/Extract
Common Mistakes
- Forgetting conditionals distribute over naked union types
- Not knowing how to switch off distribution ([T] extends [U])
- Misusing infer outside the extends clause
- Assuming extends means class inheritance rather than assignability
- Writing non-terminating recursive conditional types
Best Answer (HR Friendly)
“Conditional types are TypeScript's way of writing if/else logic for types: pick one type when a condition holds and another when it doesn't. They let types adapt automatically to the input, which powers many of TypeScript's smart built-in helpers.”
Code Example
// Basic branch
type IsString<T> = T extends string ? 'yes' : 'no'
type A = IsString<'hi'> // 'yes'
type B = IsString<number> // 'no'
// Extract a return type with infer
type MyReturnType<F> = F extends (...args: any[]) => infer R ? R : never
type R = MyReturnType<() => number> // number
// Distribution over unions
type ToArray<T> = T extends any ? T[] : never
type U = ToArray<string | number> // string[] | number[]
// Disable distribution by wrapping in a tuple
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type W = ToArrayNonDist<string | number> // (string | number)[]
// Recursive unwrap of nested Promises
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T
type Value = Awaited<Promise<Promise<boolean>>> // booleanFollow-up Questions
- How does the infer keyword work inside a conditional type?
- Why do conditional types distribute over union types?
- How do you prevent distribution when you don't want it?
- How is ReturnType implemented using conditional types?
- Can conditional types be recursive, and what are the limits?
MCQ Practice
1. What is the syntax of a conditional type?
Conditional types use T extends U ? X : Y, resolving to X when T is assignable to U and to Y otherwise.
2. What does the infer keyword do?
infer introduces a type variable inside the extends clause to capture a portion of the matched type, such as a function's return type.
3. How do you disable distribution over a union in a conditional type?
Wrapping the checked type in a tuple ([T] extends [U]) stops the naked type parameter from distributing over the union.
Flash Cards
Conditional type syntax? — T extends U ? X : Y — resolves to X if T is assignable to U, else Y.
What does infer do? — Captures part of a type inside the extends clause, e.g. a return type.
When do conditionals distribute? — When the checked type is a naked type parameter over a union.
How to stop distribution? — Wrap both sides in tuples: [T] extends [U] ? ... : ....