CHAN.RUN
function match<T>(error, handlers): T;Defined in: src/match.ts
Handle an error by matching against a handler map keyed by name or code.
Fault errors are matched by name, then by code.
Native errors (TypeError, AbortError, SyntaxError, etc.) are matched by name.
Plain Error (name === "Error") always falls through to _ to avoid catching everything.
When the error type is known (from declares + tryAsync), handlers are exhaustiveness-checked — TypeScript will error if you miss one:
const result = await tryAsync(getUser, id);
if (!result.ok) {
// TypeScript requires handlers for NotFoundError AND DbError
match(result.error, [NotFoundError, DbError], {
NotFoundError: (err) => respond(404, err.message),
DbError: (err) => respond(503, "DB unavailable"),
});
}For untyped errors, use the two-arg form with string keys + fallback:
match(error, {
ValidationError: (err) => respond(400, err.message),
_: (err) => { throw err },
});| Type Parameter |
|---|
T |
| Parameter | Type |
|---|---|
error | unknown |
handlers | MatchHandlers<T> |
T
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
match(err, {
ValidationError: (e) => res.status(400).json({ error: e.message }),
UnauthorizedError: (e) => res.status(401).json({ error: "Unauthorized" }),
NotFoundError: (e) => res.status(404).json({ error: e.message }),
_: (e) => {
console.error("Unhandled error", e);
res.status(500).json({ error: "Internal server error" });
},
});
});const result = await tryAsync(createOrder, userId, items);
if (!result.ok) {
return match(result.error, [OutOfStockError, PaymentFailedError, DbError], {
OutOfStockError: (e) => Response.json({ error: e.message }, { status: 409 }),
PaymentFailedError: (e) => Response.json({ error: "Payment failed" }, { status: 402 }),
DbError: () => Response.json({ error: "Try again later" }, { status: 503 }),
});
}async function handleSubmit(data: FormData) {
const result = await tryAsync(submitForm, data);
if (!result.ok) {
match(result.error, {
ValidationError: (e) => toast.warning(e.message),
NetworkError: () => toast.error("Check your connection"),
_: () => toast.error("Something went wrong"),
});
}
}const result = await tryAsync(() => fetch(url, { signal }));
if (!result.ok) {
match(result.error, {
AbortError: () => console.log("Request cancelled"),
TypeError: (e) => console.error("Network failure:", e.message),
_: (e) => { throw e },
});
}function reportError(error: unknown) {
match(error, {
ValidationError: (e) => metrics.increment("validation_error"),
DbError: (e) => {
logger.error("Database error", { message: e.message, code: e.code });
alertOncall("db-failure");
},
_: (e) => logger.warn("Unknown error", { error: e }),
});
}function match<TErrors, THandlers>(
error,
errorClasses,
handlers): ReturnType<Extract<THandlers[keyof THandlers], (...args) => unknown>>;Defined in: src/match.ts
Handle an error by matching against a handler map keyed by name or code.
Fault errors are matched by name, then by code.
Native errors (TypeError, AbortError, SyntaxError, etc.) are matched by name.
Plain Error (name === "Error") always falls through to _ to avoid catching everything.
When the error type is known (from declares + tryAsync), handlers are exhaustiveness-checked — TypeScript will error if you miss one:
const result = await tryAsync(getUser, id);
if (!result.ok) {
// TypeScript requires handlers for NotFoundError AND DbError
match(result.error, [NotFoundError, DbError], {
NotFoundError: (err) => respond(404, err.message),
DbError: (err) => respond(503, "DB unavailable"),
});
}For untyped errors, use the two-arg form with string keys + fallback:
match(error, {
ValidationError: (err) => respond(400, err.message),
_: (err) => { throw err },
});| Type Parameter |
|---|
TErrors extends NamedFaultErrorClass<string>[] |
THandlers extends TypedMatchHandlers<unknown, TErrors> |
| Parameter | Type |
|---|---|
error | unknown |
errorClasses | readonly [TErrors] |
handlers | THandlers |
ReturnType<Extract<THandlers[keyof THandlers], (...args) => unknown>>
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
match(err, {
ValidationError: (e) => res.status(400).json({ error: e.message }),
UnauthorizedError: (e) => res.status(401).json({ error: "Unauthorized" }),
NotFoundError: (e) => res.status(404).json({ error: e.message }),
_: (e) => {
console.error("Unhandled error", e);
res.status(500).json({ error: "Internal server error" });
},
});
});const result = await tryAsync(createOrder, userId, items);
if (!result.ok) {
return match(result.error, [OutOfStockError, PaymentFailedError, DbError], {
OutOfStockError: (e) => Response.json({ error: e.message }, { status: 409 }),
PaymentFailedError: (e) => Response.json({ error: "Payment failed" }, { status: 402 }),
DbError: () => Response.json({ error: "Try again later" }, { status: 503 }),
});
}async function handleSubmit(data: FormData) {
const result = await tryAsync(submitForm, data);
if (!result.ok) {
match(result.error, {
ValidationError: (e) => toast.warning(e.message),
NetworkError: () => toast.error("Check your connection"),
_: () => toast.error("Something went wrong"),
});
}
}const result = await tryAsync(() => fetch(url, { signal }));
if (!result.ok) {
match(result.error, {
AbortError: () => console.log("Request cancelled"),
TypeError: (e) => console.error("Network failure:", e.message),
_: (e) => { throw e },
});
}function reportError(error: unknown) {
match(error, {
ValidationError: (e) => metrics.increment("validation_error"),
DbError: (e) => {
logger.error("Database error", { message: e.message, code: e.code });
alertOncall("db-failure");
},
_: (e) => logger.warn("Unknown error", { error: e }),
});
}