Skip to content

Types

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

The result type returned when throws: false is set.

type SafeResult<T> =
| { ok: true; data: T }
| { ok: false; error: Error; status: number; body: unknown };

The base type for status-discriminated response unions.

type StatusResponse<StatusField extends string, BodyField extends string> = {
[K in StatusField]: number;
} & {
[K in BodyField]: unknown;
};

StatusRange / StatusGroup / StatusSpecifier

Section titled “StatusRange / StatusGroup / StatusSpecifier”
type StatusRange = "1xx" | "2xx" | "3xx" | "4xx" | "5xx";
type StatusGroup = "success" | "error";
type StatusSpecifier = StatusRange | StatusGroup;
type NegatedSpecifier = `!${StatusSpecifier}`;
type StatusArg =
| number
| StatusSpecifier
| NegatedSpecifier
| readonly (number | StatusSpecifier | NegatedSpecifier)[];

Constrains the success status argument. Accepts status codes, specifiers, negation, ranges, and mixed arrays.

type SuccessArg<R extends StatusResponse<SF>, SF extends string> =
| StatusOf<R, SF>
| StatusSpecifier
| NegatedSpecifier
| readonly (StatusOf<R, SF> | StatusSpecifier | NegatedSpecifier)[];

Extracts the body type for the matching success status.

type Response =
| { status: 200; body: { id: string } }
| { status: 201; body: { id: string; created: boolean } };
type Body200 = ResolveSuccessBody<Response, 200>;
// ^? { id: string }
type Body200or201 = ResolveSuccessBody<Response, [200, 201]>;
// ^? { id: string } | { id: string; created: boolean }

Type for createExpectStatus configuration options.

type ExpectStatusOptions<SF extends string, BF extends string> = {
fetcher?: (
url: string | URL,
init?: RequestInit,
) => Promise<Response | Record<string, unknown>>;
adapter?: (response: any) => StatusResponse<SF, BF>;
errorFactory?: ErrorFactory<SF, BF>;
extractMessage?: Extractor;
fallbackMessage?: string;
groups?: Record<string, number[]>;
defaults?: ExpectStatusDefaults;
onError?: (
err: Error,
response: StatusResponse<SF, BF>,
) => void | Promise<void>;
onSuccess?: (response: StatusResponse<SF, BF>) => void | Promise<void>;
statusField?: SF;
bodyField?: BF;
};

Type for instance-wide defaults. Uses flat dispatch: string values are messages, function values are handlers.

type ExpectStatusDefaults = {
[statusOrRange: string | number]: string | ((body: unknown) => unknown);
transform?: (body: unknown) => unknown | Promise<unknown>;
recover?: (error: Error) => unknown | Promise<unknown>;
};

Type for message extraction functions.

type Extractor = (body: unknown) => string | undefined;

Type for the exhaustive field in dispatch. When all error statuses are covered, resolves to boolean (accepting true). When statuses are uncovered, resolves to a branded error object naming the missing codes.

type ExhaustiveCheck<R, S, Keys, SF extends string> = [
UncoveredErrors<R, S, Keys, SF>,
] extends [never]
? boolean
: {
__expectStatusError: "Not exhaustive — missing coverage...";
missing: UncoveredErrors<R, S, Keys, SF>;
};

Utility type that expands a simple { status: BodyType } record into a status-discriminated union. Useful when you don’t have a codegen:

import { type StatusMap } from "expect-status";
type CreateOrgResponse = StatusMap<{
201: Organisation;
409: { organisationId: string };
422: { fieldErrors: Record<string, string> };
}>;
// ^ expands to:
// | { status: 201; body: Organisation }
// | { status: 409; body: { organisationId: string } }
// | { status: 422; body: { fieldErrors: Record<string, string> } }

Identity function that types dispatch handler bodies per status key. Zero runtime overhead — exists purely for TypeScript inference when you don’t have codegen types:

import { typed } from "expect-status";
const org = await expectStatus(
201,
api.createOrg(data),
typed<{
409: { organisationId: string };
422: { fieldErrors: Record<string, string> };
}>({
409: ({ organisationId }) => router.push(`/org/${organisationId}`), // ← typed
422: ({ fieldErrors }) => setFieldErrors(fieldErrors), // ← typed
}),
);