Conditional Types
Conditional types select one of two types based on a condition expressed as an extends clause, enabling type-level branching logic.
Definition
type Result<T> = T extends string ? StringResult : OtherResult;Examples
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
type C = IsString<"hello">; // truetype ToArray<T> = T extends any ? T[] : never;
type A = ToArray<string | number>;
// string[] | number[]
type NonDistributive<T> = [T] extends [any] ? T[] : never;
type B = NonDistributive<string | number>;
// (string | number)[]type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type A = TypeName<string>; // "string"
type B = TypeName<() => void>; // "function"
type C = TypeName<number[]>; // "object"Common Use Cases
- 1Type-level if/else branching logic
- 2Building type-safe overloaded function return types
- 3Filtering and transforming union types distributively
- 4Pattern matching on type shapes
- 5Creating recursive type utilities
Understanding Conditional Types
Conditional types are one of TypeScript's most powerful features. They follow the syntax T extends U ? X : Y and evaluate to X if T is assignable to U, or Y otherwise. This enables type-level branching logic that can express complex type transformations.
A key feature is distributivity over unions. When a conditional type is applied to a naked type parameter and that parameter is instantiated with a union, the conditional distributes over each union member individually. This is what makes utility types like Exclude and Extract work: Exclude<"a" | "b" | "c", "a"> distributes to ("a" extends "a" ? never : "a") | ("b" extends "a" ? never : "b") | ("c" extends "a" ? never : "c"), which simplifies to "b" | "c".
You can disable distributivity by wrapping both sides in tuples: [T] extends [U] ? X : Y. This treats T as a whole rather than distributing over its members, which is sometimes desired when you want to check the union as a unit.
Conditional types can be nested to create multi-branch logic, similar to a chain of if/else statements. This pattern is commonly used to map runtime types to string labels or to choose different processing strategies based on input shape.
Combined with the infer keyword, conditional types can extract types from within other types—like extracting the return type from a function signature or the element type from an array. This extraction capability is the foundation of utility types like ReturnType, Parameters, and InstanceType.
Related Types
Exclude<T, U>Constructs a type by excluding from T all union members that are assignable to U.
Extract<T, U>Constructs a type by extracting from T all union members that are assignable to U.
infer keywordThe infer keyword declares a type variable within a conditional type's extends clause, extracting and capturing part of a type for use in the true branch.
Mapped TypesMapped types iterate over the keys of a type to create a new type by transforming each property, enabling systematic type transformations.
More Conditional Types
Explore TypeScript Types
Browse our complete reference of 30 TypeScript utility types with definitions, examples, and explanations.