Careeroza
Result Types Instead of Throwing
Make Failure Visible in the Type
A Result<T, E> return type forces the caller to handle failure before reading the value. Use it for expected failures (invalid input, out of stock, payment declined) and keep exceptions for unexpected bugs.
Example
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
const Ok = <T>(value: T): Result<T, never> => ({ ok: true, value });
const Err = <E>(error: E): Result<never, E> => ({ ok: false, error });
type CouponError =
| { type: "NOT_FOUND" }
| { type: "EXPIRED"; on: string }
| { type: "MIN_ORDER"; min: number };
const coupons: Record<string, { percent: number; min: number; expires: string }> = {
FEST20: { percent: 20, min: 999, expires: "2026-12-31" },
OLD10: { percent: 10, min: 0, expires: "2025-01-01" },
};
function applyCoupon(code: string, cart: number, today: string): Result<number, CouponError> {
const c = coupons[code];
if (!c) return Err({ type: "NOT_FOUND" });
if (today > c.expires) return Err({ type: "EXPIRED", on: c.expires });
if (cart < c.min) return Err({ type: "MIN_ORDER", min: c.min });
return Ok(cart - (cart * c.percent) / 100);
}
for (const [code, cart] of [["FEST20", 1500], ["FEST20", 500], ["OLD10", 800], ["XYZ", 800]] as const) {
const r = applyCoupon(code, cart, "2026-01-15");
if (r.ok) console.log(code, "-> pay", r.value);
else if (r.error.type === "MIN_ORDER") console.log(code, "-> add Rs.", r.error.min - cart, "more");
else if (r.error.type === "EXPIRED") console.log(code, "-> expired on", r.error.on);
else console.log(code, "-> invalid coupon");
}Output
FEST20 -> pay 1200 FEST20 -> add Rs. 499 more OLD10 -> expired on 2025-01-01 XYZ -> invalid coupon
Forgetting to Check
Example
Reading value without checking ok is an error (TS2339), because the failure branch has no value.
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
declare function parseAmount(s: string): Result<number, string>;
const total = parseAmount("100").value + 10;Compiler output (tsc)
Line 4: error TS2339: Property 'value' does not exist on type 'Result<number, string>'.
Property 'value' does not exist on type '{ ok: false; error: string; }'.Throw or Return?
| Use exceptions for | Use Result for |
|---|---|
| bugs and impossible states | expected business failures |
| infrastructure failures (DB down) | validation errors shown to users |
| framework integration (NestJS exception filters turn them into HTTP errors) | pure domain functions that are easy to test |