TypeScript Conditional Types

Conditional types let you compute a type based on a condition, using the T extends U ? X : Y syntax, enabling powerful type-level logic like filtering, unwrapping, and inference from other types.

Basic Conditional Type Syntax

A conditional type has the form T extends U ? X : Y, evaluated entirely at the type level: if T is assignable to U, the type resolves to X, otherwise to Y.

A simple conditional type

type IsString<T> = T extends string ? true : false;

type A = IsString<"hello">; // true
type B = IsString<42>;      // false

Distributive Conditional Types

When T is a bare, naked type parameter in the condition, the conditional type distributes over a union, applying itself to each member separately and joining the results back into a union.

Distributing over a union

type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArray = ToArray<string | number>;
// string[] | number[]  (distributed member by member)

Inferring Types With infer

The infer keyword, used inside the extends clause of a conditional type, captures part of the matched type as a new type variable, letting you extract things like a Promise's resolved value or an array's element type.

Extracting nested types

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type A2 = UnwrapPromise<Promise<string>>; // string
type B2 = UnwrapPromise<number>;          // number

type ElementType<T> = T extends (infer U)[] ? U : never;
type Item = ElementType<number[]>; // number
  • Built-in utility types like ReturnType, Parameters, and Awaited are themselves built from conditional types plus infer
  • Conditional types can filter members out of a union by resolving unwanted branches to never
  • They power recursive helper types such as a DeepPartial or DeepReadonly
  • They let a function's return type depend on the shape of its input type
Utility typeBuilt fromWhat it does
ReturnType<T>T extends (...a: any) => infer R ? R : neverextracts a function's return type
Parameters<T>T extends (...a: infer P) => any ? P : neverextracts a function's parameter tuple
Exclude<T, U>T extends U ? never : Tremoves union members assignable to U
Extract<T, U>T extends U ? T : neverkeeps only union members assignable to U
Note: Deeply nested or recursive conditional types can slow the compiler down noticeably, or fail on very large unions. Keep them as shallow as the problem allows and test them against real, representative input types.

Exercise: TypeScript Conditional Types

What is the basic syntax of a TypeScript conditional type?