BeCuriouzLearn something new every day
Conditional Types: Types That Choose
💡 Conditional types branch on extends, and infer pattern-matches inside that branch to pull out a hidden type.
You already know T extends U from generic constraints. A conditional type reuses that same extends keyword as a ternary at the type level: T extends U ? X : Y means "if T is assignable to U, resolve to X, otherwise Y." It lets a type branch on its input.
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<"hi">; // "yes"
type B = IsString<42>; // "no"The real power comes from infer, which declares a placeholder type variable inside the extends clause. TypeScript pattern-matches your type against the shape and binds whatever fills the hole. Want the element type of an array, or a function's return type? Match the shape and capture the piece you need:
type ElementOf<T> = T extends (infer E)[] ? E : never;
type N = ElementOf<number[]>; // number
type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never;
type R = ReturnOf<() => string>; // stringThis is exactly how built-in utilities like ReturnType and Parameters are implemented. One subtlety: when the checked type T is a naked type parameter and you pass a union, the conditional distributes over each member — IsString becomes IsString = "yes" | "no". Wrapping both sides in a tuple ([T] extends [U]) turns distribution off.