TYPESCRIPT / CHAPTER 05

Narrowing and explicit states

Intermediate

Represent 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.

TypeScript example
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}));
Result
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

Chapter checkpoint

Which property best discriminates success from failure?