Glossary / Discriminated unions
Discriminated unions
A discriminated union is a union of object types that share a property whose literal value identifies the variant. TypeScript uses that discriminant to narrow the remaining properties, making state-dependent data explicit and enabling exhaustive handling.
Modeling and narrowing
type Result<T> =
| { status: 'ok'; value: T }
| { status: 'error'; error: Error };
function describe<T>(result: Result<T>) {
if (result.status === 'ok') return String(result.value);
return result.error.message;
}
The discriminant should be stable and uniquely identify variants. switch statements are often clear. An exhaustive never check makes additions to the union visible at compile time:
function assertNever(value: never): never { throw new Error(String(value)); }
After all cases, passing the residual value to assertNever errors if a new variant was added but not handled. TypeScript can lose narrowing when values are mutated, aliased in unsupported ways, or passed through generic abstractions; immutable data and narrow control flow help.
Discriminated unions model valid state combinations, preventing impossible combinations such as loading: true alongside stale error and success fields. They do not validate JSON: parse external input as unknown and check the tag and payload at runtime.
Interview questions
- What is the discriminant? A shared property with literal values that identifies each union member.
- How enforce exhaustive handling? Check the residual value as
neverafter covering all variants. - Why preferable to several booleans? It can encode mutually exclusive states and prevent invalid combinations.
- Does a typed union validate network data? No; runtime validation is required.
- What can weaken narrowing? Mutation, broad types, or control flow the checker cannot prove.