Typed Errors
Make failure explicit in types so callers handle errors intentionally.
The issue
When a function throws, the type signature usually only describes the "happy path". That means callers can't know (or be forced to handle) the failure modes without reading implementation details.
type = { : string; : number };
class extends {}
class extends {}
// The signature only describes the happy path.
async function (: string, : number): <> {
if (!) throw new ("userId required");
if ( <= 0) throw new ("amount must be > 0");
if ( > 100) throw new ("insufficient funds");
return { : "PAY-123", };
}
processPayment;
async function () {
try {
const = await ("USER-1", 150);
return ;
} catch () {
error;
if ( instanceof ) return "Bad request";
if ( instanceof ) return "Payment declined";
return "Unknown error";
}
}
();What to prefer
Represent failure as a value (e.g. Result) so the failure path is explicit and composable:
import { } from "@carbonteq/fp";
type = { : string };
type =
| { : "ValidationError"; : string }
| { : "InsufficientFunds"; : string };
function (: string, : number): <, > {
if (!) return .({ : "ValidationError", : "userId required" });
if ( <= 0) return .({ : "ValidationError", : "amount must be > 0" });
if ( > 100) return .({ : "InsufficientFunds", : "insufficient funds" });
return .({ : "PAY-123" });
}
const = ("USER-1", 0);
res;
const =
.(() => .(`[PaymentError] ${.}: ${.}`))
.({
: () => `Paid: ${.}`,
: () => .,
});
message;Now the caller:
- Sees the error type in the signature — no guessing what might throw
- Is guided by the compiler —
matchrequires handling bothOkandErr - Can log or transform errors inline with
tapErrormapErr