TYPESCRIPT / CHAPTER 05
Narrowing and explicit states
IntermediateRepresent alternatives so invalid combinations are difficult to express.
By the end of this chapter
- Narrow unions using runtime checks
- Use a discriminant to connect state and data
- Check exhaustiveness with never
The idea
A union describes alternatives, so an operation must be valid for every remaining possibility unless the code narrows the value first. Runtime checks such as typeof and equality tests provide evidence the checker can follow. A discriminated union gives each variant a shared property with a different literal value, connecting a state name to the fields that are valid in that state.
This is especially useful for loading, success, and failure results. Separate variants avoid records containing several unrelated optional properties that allow impossible combinations. A switch over the discriminant reveals the corresponding fields. An exhaustive check using never can make a newly added variant produce a diagnostic where handling is incomplete. The check protects maintenance, while real runtime validation remains necessary for external data.
type Result = {kind: "ok"; value: number} | {kind: "error"; message: string};
function describe(result: Result): string {
if (result.kind === "ok") return String(result.value);
return result.message;
}
console.log(describe({kind: "ok", value: 6}));6
Checking kind narrows result to the success variant. In the other branch, the message property is known to exist.
Try it yourself
Model pending, ready-with-items, and failed-with-message states, then write an exhaustive label function.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING