A discriminated union gives each member of a union a shared literal-type field (the 'tag' or 'discriminant'), letting TypeScript narrow precisely based on that field.
ts
type Action =
| { type: "deposit"; amount: number }
| { type: "withdraw"; amount: number }
| { type: "transfer"; to: string; amount: number };
function apply(a: Action): string {
switch (a.type) {
case "deposit": return `+${a.amount}`;
case "withdraw": return `-${a.amount}`;
case "transfer": return `-> ${a.to}: ${a.amount}`;
}
}Inside each `case`, TypeScript narrows `a` to exactly that variant, so `a.to` is only accessible in the `"transfer"` branch. This pattern scales well for modelling transaction types, wallet events, or reducer actions.