Exhaustiveness Checking
expect-status supports compile-time exhaustiveness checking to ensure all error statuses in your response union are covered by handlers or messages.
Basic usage
Section titled “Basic usage”Set exhaustive: true to enable exhaustiveness checking:
type Response = | { status: 200; body: { id: string } } | { status: 404; body: { message: string } } | { status: 409; body: { message: string; id: string } };
await expectStatus(200, response, { 404: (body) => { /* handle 404 */ }, 409: (body) => { /* handle 409 */ }, exhaustive: true,});If you miss a status, TypeScript will error:
await expectStatus(200, response, { 404: (body) => { /* handle 404 */ }, // Missing 409 - type error! exhaustive: true,});Using messages
Section titled “Using messages”String entries also count toward exhaustiveness — coverage means an exact entry or a matching range:
await expectStatus(200, response, { 404: (body) => { /* handle 404 */ }, 409: "Conflict", exhaustive: true,});Range matchers
Section titled “Range matchers”Range matchers like '4xx' count as covering all statuses in that class:
await expectStatus(200, response, { "4xx": (body) => { /* handles 404, 409, and all other 4xx */ }, exhaustive: true,});With instance defaults
Section titled “With instance defaults”Instance-wide defaults count toward coverage. exhaustive itself is per-call only.
const expectStatus = createExpectStatus({ defaults: { "5xx": "Server error", },});
await expectStatus(200, response, { 404: (body) => { /* ... */ }, 409: (body) => { /* ... */ }, exhaustive: true,});When to use exhaustiveness
Section titled “When to use exhaustiveness”Exhaustiveness checking is useful when:
- You want to ensure all error cases are handled
- You’re building a library or framework where completeness matters
- You want compile-time safety for error handling
When not to use exhaustiveness
Section titled “When not to use exhaustiveness”Skip exhaustiveness when:
- You’re prototyping and don’t want to handle every error yet
- You have a global error handler that catches everything
- Your API has many error statuses and you only care about specific ones
Runtime guard
Section titled “Runtime guard”A runtime guard also fires if exhaustive: true is bypassed at the type level (e.g. via as any) — surfacing the gap loudly rather than silently degrading to extractMessage / fallbackMessage.
See also
Section titled “See also”- Flat Dispatch — handlers and messages
- Error Resolution — the full priority order
- createExpectStatus — API reference