BeCuriouzBeCuriouz

Learn something new every day

🔷 TypeScript
🔀

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.

ts
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:

ts
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>;  // string

This 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 | IsString = "yes" | "no". Wrapping both sides in a tuple ([T] extends [U]) turns distribution off.

🔀 Conditional Types: Types That Choose — BeCuriouz