Types in Motion
Narrowing & discriminated unions
Lesson 1 of 3
What you'll learn
- Understand how TypeScript narrows a union as you test it
- Model "one of several shapes" with a discriminated union
- Use an exhaustive
switchthat the compiler checks for you
A union type says a value is one of several things: string | number. On its own that's not very useful — you can't call .toFixed() on something that might be a string. Narrowing is how you earn access to the specific type: as you test a value, TypeScript shrinks the union.
function format(value: string | number) {
if (typeof value === "number") {
return value.toFixed(2); // here, value is `number`
}
return value.trim(); // here, value is `string`
}
Discriminated unions
The most powerful pattern in everyday TypeScript is the discriminated union: several object shapes that share a literal "tag" field. The tag lets the compiler tell them apart.
type Result =
| { status: "ok"; data: string }
| { status: "error"; message: string };
When you switch on the tag, each branch narrows to exactly one shape — so result.data is only reachable when status === "ok".
Exhaustiveness checking
Assign the value to a never in the default branch. If you later add a new variant and forget to handle it, the code stops compiling — the compiler becomes your checklist.
The challenge models an API result. Run it, then try adding a third variant to the union and watch where you'd need to handle it.
Run it. Each branch sees only the fields that exist for that status.
Next: writing functions that work for any type without losing type safety.
Sign in to save your progress across devices.