TypeScript Conditional Types
Branch on type relationships with extends, infer, and distribution
Overview
Conditional types choose one of two types based on a compile-time relationship, using the form T extends U ? X : Y. Combined with the infer keyword they can extract and reshape parts of a type, and they distribute over unions automatically. They power many of the built-in utility types.
Syntax / Usage
The extends clause is a type-level assignability test, not runtime code. infer introduces a fresh type variable captured from the matched position.
// Basic conditional
type IsString<T> = T extends string ? true : false
type A = IsString<"hi"> // true
type B = IsString<number> // false
// infer extracts the element type of an array
type ElementOf<T> = T extends (infer E)[] ? E : never
type C = ElementOf<number[]> // number
// infer the resolved value of a Promise
type Awaited2<T> = T extends Promise<infer V> ? V : T
type D = Awaited2<Promise<string>> // string
Examples
Conditional types distribute over each member of a union.
type NonNullish<T> = T extends null | undefined ? never : T
type Clean = NonNullish<string | null | undefined> // string
Extract a function's return type by inferring the position after =>.
type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never
function load() {
return { id: 1, ok: true }
}
type Loaded = ReturnOf<typeof load> // { id: number; ok: boolean }
Wrap distribution in a tuple to compare the whole union at once.
// [T] prevents distribution, so this tests the union as a single type
type IsExactlyString<T> = [T] extends [string] ? true : false
type E = IsExactlyString<string | number> // false
Common Mistakes
- Forgetting that naked type parameters distribute over unions, changing results
- Placing
inferoutside a conditional type, where it is not allowed - Assuming
extendsruns at runtime—it is purely a type-level check - Nesting deep conditionals that hurt readability and compile performance
- Returning
anyfrom the false branch instead of a precisenever
See Also
typescript-mapped-types generics utility-types