2 min read

TypeScript Conditional Types

Conditional types allow you to define types that depend on other types. They take a form that looks like a ternary operator in JavaScript.

1. Syntax

SomeType extends OtherType ? TrueType : FalseType;

If SomeType is assignable to OtherType, then the type resolves to TrueType, otherwise it resolves to FalseType.

2. Basic Usage

interface Animal {
  live(): void;
}
interface Dog extends Animal {
  woof(): void;
}

type Example1 = Dog extends Animal ? number : string;
// type Example1 = number

type Example2 = RegExp extends Animal ? number : string;
// type Example2 = string

3. Distributive Conditional Types

When conditional types act on a generic type, they become distributive when given a union type. This means the condition is applied to each member of the union individually.

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

type StrArrOrNumArr = ToArray<string | number>;
// Result: string[] | number[]

If we didn't want this distribution, we would wrap the extends keywords in square brackets:

type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
type StrOrNumArr = ToArrayNonDist<string | number>;
// Result: (string | number)[]

4. Filtering Types (Exclude/Extract)

Conditional types are often used to filter unions by returning never for types you want to remove. Since never is ignored in unions, this effectively removes the type.

// Exclude null and undefined from T
type MyNonNullable<T> = T extends null | undefined ? never : T;

type T0 = MyNonNullable<string | number | undefined>;
// type T0 = string | number

This is how the built-in Exclude<T, U> utility type works:

type MyExclude<T, U> = T extends U ? never : T;

type T1 = MyExclude<"a" | "b" | "c", "a">;
// "b" | "c"

5. Inferring Within Conditional Types (infer)

You can use the infer keyword to deduce a type within the condition. This is useful for unwrapping types (like getting the return type of a function or the element type of an array).

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

type Str = Flatten<string[]>;
// type Str = string

type Num = Flatten<number>;
// type Num = number

programming/javascript/typescript/typescript