Reference
yieldless/error
Tuple-based error handling primitives.
yieldless/error is the smallest useful piece of the library. It gives you a single tuple shape and a few helpers for converting thrown code into that shape.
Exports
type SafeResult<T, E = Error> = [E, null] | [null, T]ok(value): SafeResult<T, never>err(error): SafeResult<never, E>safeTry(promise): Promise<SafeResult<T>>safeTrySync(fn): SafeResult<T>match(result, { ok, err }): Returnunwrap(result): T
Typical use
import { err, match, ok, safeTry, safeTrySync, unwrap } from "yieldless/error";
const [readError, body] = await safeTry(readFile("package.json", "utf8"));
if (readError) {
return err(readError);
}
const parsed = safeTrySync(() => JSON.parse(body));
const value = unwrap(parsed);
const state = match(ok(value), {
ok: (data) => ({ kind: "ready", data }),
err: (error) => ({ kind: "error", message: String(error) }),
});When to use it
- Wrapping filesystem, HTTP, database, or subprocess calls
- Converting parse and validation failures into explicit branches
- Leaving framework boundaries as tuples until the last possible moment, then folding them with
match()
Rules of thumb
- Prefer
safeTry()at the boundary, not around every individual expression. - Use
ok()anderr()when returning tuples so the intent reads clearly in application code. - Keep the tuple local. Once you have the success value, use the value or fold it with
match(). - Use
unwrap()only where a thrown exception is genuinely required.
Good
Wrap the operation that can fail, check the error slot once, then continue with normal values.
const [readError, text] = await safeTry(readConfigFile());
if (readError) {
return err(readError);
}
const [parseError, config] = safeTrySync(() => JSON.parse(text));
if (parseError) {
return err(parseError);
}
return ok(config);Use match() when crossing into UI state or framework output.
const view = match(result, {
ok: (user) => ({ status: "ready" as const, user }),
err: (error) => ({ status: "failed" as const, message: String(error) }),
});Avoid
Do not ignore the error slot just because a type checker lets you.
const result = await safeTry(readConfigFile());
const config = JSON.parse(result[1] as string);Do not use unwrap() as a replacement for tuple handling in service code.
const user = unwrap(await safeTry(loadUser(id)));
return ok(user);Caveat
SafeResult uses null sentinels. If your success value is literally null, the runtime tuple is still correct, but the type system cannot fully discriminate that case.