Errors as values
Ordinary errors are returned in a Result
A small, focused Result type with a separate defect channel for the unexpected â and qualification enforced at every boundary.
import { ok, err, fromPromise, type Result } from "unthrown";
class NotFound extends TaggedError("NotFound") {}
function findUser(id: string): Result<User, NotFound> {
const user = users.get(id);
return user ? ok(user) : err(new NotFound());
}
// Cross an async boundary â every rejection MUST be triaged.
const profile = fromPromise(fetch(`/u/${id}`), (cause) =>
cause instanceof Response ? new NotFound() : defect(cause),
);
// Handle every channel once, at the edge â no surrounding try/catch.
const status = await profile.match({
ok: () => 200,
err: () => 404,
defect: () => 500,
});Ordinary errors travel as values through map / flatMap / match. A thrown bug becomes a defect that short-circuits to the edge â never silently folded into your domain errors.