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