Skip to content

Type-Level Patterns

expect-status exports several TypeScript types for advanced type-level programming and custom utilities.

Extracts the body type for the matching success status.

import type { ResolveSuccessBody } from "expect-status";
type Response =
| { status: 200; body: { id: string } }
| { status: 201; body: { id: string; created: boolean } }
| { status: 404; body: { message: string } };
type Body200 = ResolveSuccessBody<Response, 200>;
// ^? { id: string }
type Body200or201 = ResolveSuccessBody<Response, [200, 201]>;
// ^? { id: string } | { id: string; created: boolean }

Constrains the success status argument to valid status codes from the response type.

import type { SuccessArg } from "expect-status";
type Response =
| { status: 200; body: { id: string } }
| { status: 404; body: { message: string } };
type ValidSuccess = SuccessArg<Response, "status">;
// ^? 200 | 404 | readonly (200 | 404)[]

Extracts the error statuses (non-success) from a response type. Works with single or array success arguments.

import type { ResolveErrorStatus } from "expect-status";
type Response =
| { status: 200; body: { id: string } }
| { status: 404; body: { message: string } }
| { status: 409; body: { message: string } };
type Errors = ResolveErrorStatus<Response, 200>;
// ^? 404 | 409
type MultiErrors = ResolveErrorStatus<Response, readonly [200]>;
// ^? 404 | 409

Maps three-digit statuses to their HTTP class.

import type { StatusToClass } from "expect-status";
type Class200 = StatusToClass<200>;
// ^? '2xx'
type Class404 = StatusToClass<404>;
// ^? '4xx'
type Class500 = StatusToClass<500>;
// ^? '5xx'

Type-level check for whether a numeric status is covered by an exact match in Keys or by its class-level range being in Keys.

import type { IsCovered } from "expect-status";
type Covered = IsCovered<200, 200 | 404>;
// ^? true
type NotCovered = IsCovered<409, 200 | 404>;
// ^? false
type RangeCovered = IsCovered<404, 200 | "4xx">;
// ^? true (404 falls under '4xx')

Lists error statuses that are not covered by a set of handler/message keys.

import type { UncoveredErrors } from "expect-status";
type Response =
| { status: 200; body: { id: string } }
| { status: 404; body: { message: string } }
| { status: 409; body: { message: string } };
// Success is 200, keys cover only 404 — 409 is uncovered
type Uncovered = UncoveredErrors<Response, 200, 404>;
// ^? 409
// '4xx' range covers both 404 and 409
type AllCovered = UncoveredErrors<Response, 200, "4xx">;
// ^? never

Resolves to boolean if all error statuses are covered (allowing exhaustive: true). Otherwise resolves to a branded error object naming the missing codes.

import type { ExhaustiveCheck } from "expect-status";
type Response =
| { status: 200; body: { id: string } }
| { status: 404; body: { message: string } }
| { status: 409; body: { message: string } };
type NotExhaustive = ExhaustiveCheck<Response, 200, 404>;
// ^? { __expectStatusError: '...'; missing: 409 }
type Exhaustive = ExhaustiveCheck<Response, 200, 404 | 409>;
// ^? boolean

You can use these types to build custom type-level utilities:

import type { ResolveSuccessBody, ResolveErrorStatus } from "expect-status";
type ApiClient<R, S> = {
request<T>(path: string): Promise<ResolveSuccessBody<R, S>>;
errors: ResolveErrorStatus<R, S>;
};
type MyClient = ApiClient<
| { status: 200; body: { id: string } }
| { status: 404; body: { message: string } },
200
>;
// MyClient.errors = 404