1. Introduction
Conditional types let you express type-level if/else logic: given a type T, check whether it is assignable to U, and select one of two resulting types accordingly. This single mechanism powers much of TypeScript's advanced type inference, including built-in utilities like Exclude, Extract, ReturnType, and Awaited. Conditional types become especially powerful when combined with generics, because the branch chosen can depend on a type parameter supplied by the caller of a generic function or type.
Cricket analogy: A selector's rule 'if a player extends Allrounder, pick BattingSlot, else pick BowlingSlot' is type-level if/else logic — the same mechanism behind built-in selection rules like picking specialists or excluding injured players from a squad, most powerful when the rule depends on which player a captain supplies.
2. Syntax
type ConditionalType<T> = T extends U ? TrueBranch : FalseBranch;
// A concrete example
type IsString<T> = T extends string ? "yes" : "no";
// Conditional type with inferred type using `infer`
type ElementType<T> = T extends (infer U)[] ? U : T;
// Distributive over a union (naked type parameter)
type ToArray<T> = T extends unknown ? T[] : never;
// Non-distributive: wrap T in a tuple to opt out of distribution
type ToArrayNonDist<T> = [T] extends [unknown] ? T[] : never;3. Explanation
A conditional type T extends U ? X : Y evaluates the extends-check structurally: if every value assignable to T is also assignable to U, the type resolves to X, otherwise Y. When T is a 'naked' type parameter (used bare, not wrapped in an array, tuple, or another generic) and the type actually supplied at the call site is a union, TypeScript applies the conditional type to each union member separately and unions the results back together — this is called a distributive conditional type.
Cricket analogy: Given a naked selection rule applied to Batter | Bowler, TypeScript doesn't check the combined pair at once — it distributes, evaluating the rule separately for Batter and for Bowler, then unions the two separate selection outcomes back together.
For example, ToArray<string | number> does not evaluate (string | number) extends unknown ? (string|number)[] : never as one step; instead it distributes into ToArray<string> | ToArray<number>, giving string[] | number[]. To suppress distribution and treat the union as a single whole, wrap both the checked type and the extends target in a tuple ([T] extends [U]), which is exactly how ToArrayNonDist<string | number> becomes (string | number)[] instead.
Cricket analogy: ToKit<Batter | Bowler> doesn't evaluate the pair as one combined kit request — it distributes into ToKit<Batter> | ToKit<Bowler>, giving separate batting and bowling kits; wrapping both sides in brackets like [Player] extends [Batter|Bowler] forces one single combined-kit evaluation instead.
The infer keyword can only appear inside the extends clause of a conditional type. It introduces a new type variable that TypeScript solves for by structural pattern matching, which is how ReturnType<T>, Parameters<T>, and Awaited<T> extract nested types without you writing the matching logic by hand.
Gotcha: distribution only happens for a bare, unmodified type parameter. If you write T extends any ? T[] : never and call it with a union, you get the distributed (union of arrays) result — but if you accidentally wrap T (e.g. compare [T] instead of T) you silently switch to non-distributive behavior, which is a common source of confusing type mismatches when refactoring generic type-level helpers.
4. Example
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
function describe<T>(
value: T
): T extends string ? "string" : T extends number ? "number" : "other" {
return (
typeof value === "string"
? "string"
: typeof value === "number"
? "number"
: "other"
) as any;
}
console.log(describe("hi"));
console.log(describe(42));
console.log(describe(true));5. Output
string
number
other6. Key Takeaways
- Conditional types follow the form
T extends U ? X : Y, evaluated structurally at the type level. - Distributive conditional types auto-distribute over each member of a union when T is a naked type parameter.
- Wrapping T and U in tuples (
[T] extends [U]) disables distribution and treats a union as one whole type. inferinside the extends clause captures and names a sub-type for use in the true branch.- Built-ins like
Exclude,Extract,ReturnType, andAwaitedare all conditional types under the hood.
Practice what you learned
1. What does `type ToArray<T> = T extends unknown ? T[] : never;` produce for `ToArray<string | number>`?
2. How do you prevent a conditional type from distributing over a union type?
3. Where can the `infer` keyword legally appear?
4. What is the result type of `IsString<string>` given `type IsString<T> = T extends string ? "yes" : "no";`?
5. Gotcha: `Exclude<T, U>` is distributive. What would `Exclude<string | number | boolean, string>` evaluate to?
Was this page helpful?
You May Also Like
Mapped Types in TypeScript
Transform every property of an existing type into a new type using `{ [K in keyof T]: ... }`, with modifiers and key remapping.
keyof and typeof Operators in TypeScript
Use `keyof T` to get a union of a type's property names, and `typeof x` in a type position to extract the type of a value.
Generic Constraints in TypeScript
Learn how to restrict generic type parameters with `extends` so generic code can safely rely on specific properties or shapes.
Utility Types in TypeScript
Learn TypeScript's built-in generic utility types — Partial, Required, Readonly, Pick, Omit, and Record — for transforming existing types.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics