Intermediate · 14 min
Discriminated unions
Model distinct states and narrow them with a shared literal property.
Describe complete states
Give each member of a union a shared property with a different literal value. Other properties can then belong only to the states that need them. This avoids an object with optional data and error fields that can represent contradictory states.
type Result = { kind: "ok"; value: number } | { kind: "error"; message: string };
function describe(result: Result): string {
return result.kind === "ok" ? String(result.value) : result.message;
}
console.log(describe({ kind: "ok", value: 8 }));Output
8
Narrow before reading
Checking the shared property narrows the entire object. Only the error branch has a message; only the success branch has a value. Keep the discriminant and its associated data together.
type Load = { state: "loading" } | { state: "ready"; count: number };
function label(load: Load): string {
if (load.state === "loading") return "Waiting";
return String(load.count);
}
console.log(label({ state: "loading" }));Output
Waiting
Put it into practice
- Read the types and predict the output before running the examples.
- Fix the task without using "any" or a type assertion, then check both practice questions.
Try it yourself
Code runs on this device. When you are signed in, drafts sync to your account.
Handle both reply states
Fix "describe" so it returns the numeric value as text for a successful reply, or the message for an error. Keep both variants and both calls.
Check the literal "kind" property before reading a property that belongs to one variant.
Show solution
type Reply = { kind: "ok"; value: number } | { kind: "error"; message: string };
function describe(reply: Reply): string {
return reply.kind === "ok" ? String(reply.value) : reply.message;
}
const summary = describe({ kind: "ok", value: 9 }) + " / " + describe({ kind: "error", message: "Retry" });
console.log(summary);The discriminant check narrows each branch. Both calls are handled, so an error reply no longer tries to read a missing value.