Conditional Types

`T extends U ? A : B` — `if` for Types

Conditional Types

Conditional types pick one of two types based on whether a condition holds. The foundation of advanced type manipulation.

4 min read Level 3/5 #typescript#conditional-types#advanced
What you'll learn
  • Read conditional-type syntax
  • Use them with generics
  • Recognize distribution over unions (next lesson)

A conditional type is a type-level ternary: T extends U ? A : B.

The Basics

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

type A = IsString<"hi">;      // true
type B = IsString<42>;         // false
type C = IsString<string>;    // true (string is assignable to string)

extends here means “is assignable to”. When the condition holds, the type is A; otherwise B.

A Common Pattern — Default Extraction

type NonNull<T> = T extends null | undefined ? never : T;

type A = NonNull<string | null>;        // string  (null → never, filtered out)
type B = NonNull<number | undefined>;   // number

A simple version of the built-in NonNullable<T>.

Pulling the Function Return Type

type ReturnTypeOf<T> = T extends (...args: any) => infer R ? R : never;

type R = ReturnTypeOf<() => User>;   // User

infer R lets you extract a type from inside the matched pattern. Full lesson next.

Nested Conditionals

type Width<T> =
  T extends string ? "short" :
  T extends number ? "exact" :
  T extends boolean ? "binary" :
  "other";

type A = Width<"hi">;     // "short"
type B = Width<42>;        // "exact"
type C = Width<true>;      // "binary"
type D = Width<symbol>;    // "other"

Stacking conditionals reads like a type-level switch.

With Generics

The most useful form — conditional + generic:

function unwrap<T>(value: T): T extends Promise<infer U> ? U : T {
  // ...
  return value as any;
}

unwrap("hi");                          // string
unwrap(Promise.resolve(42));            // number

The return type depends on what was passed.

When To Use Conditionals

  • Returning different types based on input
  • Filtering union members
  • Building utility types (most of Partial, Pick, etc., use them internally)
  • Extracting parts of types via infer

Up Next

infer in depth.

`infer` →