advanced · 25 minute study
Conditional types are type-level decisions
Read utility types intelligently and know when distributive behavior creates a surprising union.
Useful preparation · 1 prerequisite
Use this as a map, not a lock. If these ideas are familiar, continue.
- Derive contracts without copying fields — Copied DTOs drift. Mapped types and utility types derive related shapes while making their limitations visible.
First, build a mental model
A conditional type chooses an output based on assignability. When the checked side is a naked type parameter, a union is often processed one member at a time.
- T extends U is a type compatibility question, not a runtime instanceof test.
- A naked T distributes over union members.
- Wrapping both sides in tuples asks about the union as a whole.
- infer introduces a type variable in a matching position; never can remove a union member.
type AsArray<T> = T extends unknown ? T[] : never;
type WholeArray<T> = [T] extends [unknown] ? T[] : never;
type A = AsArray<string | number>;
type B = WholeArray<string | number>;
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
type Player = { id: string };
type P = ElementOf<readonly Player[]>;- Distribute
AsArray applies separately to string and number.
- Whole union
WholeArray keeps string | number together.
- Infer
ElementOf binds E to the array's element type.
Production thinking
Take this beyond the example.
Use library utility types where they already express the contract. Keep recursive types bounded and watch compiler performance on large unions.
Sources and further reading
Your evidence
unseen is a local learning signal, not a credential or proof of production competence.
Explain the mental model, then explicitly acknowledge it. Opening a page is not evidence.
Supported retries can complete this lesson. Independent evidence and delayed review determine the skill stage.
Next lesson · Keep useful literals without making false assertions →