CPCODELAB
TypeScript

Conditional Types

12 min read

Conditional Types

A conditional type selects one of two types based on a type-level condition: T extends U ? TrueType : FalseType. If T is assignable to U, the result is TrueType; otherwise it is FalseType.

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

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

// Conditional types distribute over union members
type NonNullable<T> = T extends null | undefined ? never : T;

type D = NonNullable<string | null | undefined>; // string

The `infer` keyword

infer declares a type variable inside the extends clause that TypeScript fills in when the condition matches. This is how ReturnType and Awaited are implemented.

ts
// Extract the return type of any function
type ReturnType<T extends (...args: any[]) => any> =
  T extends (...args: any[]) => infer R ? R : never;

// Extract the element type from an array
type ElementType<T> = T extends (infer E)[] ? E : never;

type E = ElementType<string[]>; // string
type F = ElementType<number[]>; // number

// Unwrap a Promise
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T;

type G = Awaited<Promise<Promise<number>>>; // number

Conditional types are evaluated lazily when the type parameter is generic. When the type parameter is a concrete (non-generic) type, distribution happens immediately. This distinction matters when writing higher-order generic utilities.

Ready to test yourself?
Practice TypeScript— quiz & coding exercises