0 day streak
javascript / expert
Snippet

Exhaustive Union Narrowing for Reactive Form States

Uses a discriminated union to model every reachable state of a form submission lifecycle, then leans on TypeScript's control-flow narrowing inside a switch statement to make each branch's extra fields (recordId, message, retryable) available without casting. The assertNever helper turns an unhandled case into both a compile-time error, if a new state variant is added later, and a runtime guard.

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type FormState =
| { status: 'idle' }
| { status: 'submitting' }
| { status: 'success'; recordId: string }
| { status: 'error'; message: string; retryable: boolean };
function assertNever(value: never): never {
throw new Error(`Unhandled form state: ${JSON.stringify(value)}`);
}
function describeState(state: FormState): string {
switch (state.status) {
case 'idle':
return 'Ready to submit';
case 'submitting':
return 'Submitting…';
case 'success':
return `Saved as ${state.recordId}`;
case 'error':
return state.retryable ? `Retry: ${state.message}` : `Failed: ${state.message}`;
default:
return assertNever(state);
}
}
angular
Breakdown
1
function assertNever(value: never): never
Accepts only the `never` type, so if a new FormState variant is added without a matching case, TypeScript flags the call site as a type error at the default branch.
2
case 'error':\n return state.retryable ? ... : ...;
Within this case, TypeScript narrows `state` to the error variant specifically, so `retryable` and `message` are accessible without optional chaining.
3
default:\n return assertNever(state);
The exhaustiveness check — reachable only if a status value slips through that no case handles, at which point it throws with the offending payload.