Patterns
How do I make success data available only in the success branch and errors only in the failure branch?
Give every variant one shared literal field. Checking it narrows the whole object, not just the field.
The recipe
type Result<T> = | { status: 'success'; data: T } | { status: 'failure'; error: Error } function unwrap<T>(result: Result<T>): T { if (result.status === 'success') { return result.data } throw result.error }
The build compiles this and checks each result below.
How it works
- 01
status: 'success'A literal discriminant identifies this member unambiguously.
- 02
if (result.status === 'success')Control-flow analysis now knows every other property on the selected variant.
What you get
Result<number>['status']
→"success" | "failure"Extract<Result<number>, { status: 'success' }>
→{ status: "success"; data: number; }ReturnType<typeof unwrap<string>>
→string
Where it goes wrong
Optional fields on one broad object do not create the same correlation. `status: "success" | "failure"` beside optional `data` and `error` still lets impossible combinations through.
Takeaway
Use a union of complete states, not one object full of optional state fragments.
