Conditional types let you write if/else logic at the type level. They power every utility type you already use — ReturnType, Exclude, Awaited, Parameters — and once you understand the three core mechanics (extends checks, infer, and distribution over unions), you can build type utilities that eliminate entire categories of bugs from your codebase.
Master typescript conditional types
Take the TypeScript Type System Mastery course with hands-on lessons and challenges.
The infer keyword captures the generic argument inside ApiResponse. This is useful when your API client returns wrapped responses and you need the inner type for components or tests without manually extracting it.
Combines key remapping (filtering keys starting with 'on') with infer to extract handler argument types. This pattern shows up in component testing utilities and form libraries where you need to programmatically call handlers with correctly typed arguments.
Extract distributes over the union and keeps only members assignable to the filter shape. This is the standard approach for narrowing discriminated unions in event systems, Redux actions, or webhook payload handlers.
Recursive conditional types keep unwrapping until the base case is reached. The built-in Awaited type works this way. The OrderService example shows how to derive a resolved return type from a class method — useful for typing service layers without exporting extra interfaces.
Wrapping both sides of extends in a tuple stops TypeScript from distributing over union members. The IsNever example is the canonical case where this matters — without the wrapper, never distributes to zero iterations and the result is never instead of true.
The mapped type checks each key's value against the target type, replacing non-matching keys with never. The indexed access [keyof T] collapses the result into a union of the surviving keys. This pattern is common in form libraries and ORM query builders where you need to select fields by type.
Expecting a union to be checked as a whole, then getting surprised by distributive behavior — e.g., `IsString<string | number>` returning `boolean` instead of `false`
Wrap both sides in a tuple when you want to check the entire union: `[T] extends [string] ? true : false`. Distribution is the default for naked type parameters. Learn to recognize when you want it and when you do not.
Using `infer` outside of an `extends` clause — e.g., writing `type X = infer T` or trying to use it in a mapped type directly
The `infer` keyword only works inside the condition of a conditional type (`T extends ... infer R ... ? ... : ...`). There is no other valid position for it. If you need type extraction elsewhere, write a conditional type helper and call it.
Writing complex nested conditional chains (3+ levels deep) that become unreadable — e.g., `T extends A ? X : T extends B ? Y : T extends C ? Z : W`
Extract each branch into a named type alias. Instead of one giant conditional, write `type HandleA<T> = ...`, `type HandleB<T> = ...`, then compose them. Readability matters more than cleverness at the type level too.
Forgetting that `never` is the empty union — passing `never` to a distributive conditional returns `never` (zero iterations), not the false branch
If your utility might receive `never`, use the tuple wrapper: `[T] extends [never] ? FallbackType : ...`. This is especially important for utilities consumed by other generic types where `never` can propagate unexpectedly.
Conditional types add branching logic to the type system with `T extends U ? X : Y`. The `infer` keyword extracts types from within complex structures — promise contents, function signatures, tuple elements. Distributive behavior automatically maps over union members, which is how `Exclude` and `Extract` work. Wrap in `[T] extends [U]` when you need to check a union as a whole. In practice, you will use conditional types most often for API response unwrapping, discriminated union filtering, and deriving types from existing code without manual duplication.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.