Result & Errors
Massaman treats expected failure as data. A fallible operation returns a Result<T> so its caller can see and handle both outcomes without relying on an exception path.
Thrown and rejected values still exist at unsafe boundaries. attempt, attemptAsync, and err normalize those values into an Error before returning an Err.
Why it matters
Exceptions hide failure outside a function's return type. A Result makes failure visible, keeps it local to the data flow, and gives TypeScript a discriminated union to narrow.
This creates a clear boundary:
- unsafe code may throw or reject
attemptorattemptAsynccatches that outcome once- application code handles
OkandErras ordinary values
Core tools
AbortError and TimeoutError provide recognizable error types for cancellation and time limits. They can be carried by an Err like any other Error.
When to use it
Use Result when failure is expected and the caller can respond:
- parsing untrusted input
- reading files or configuration
- calling a network or storage boundary
- enforcing a domain rule that can reject a value
Construct ok and err in functions that model fallibility directly. Use attempt and attemptAsync around APIs that communicate failure by throwing or rejecting.
Complete example
The unsafe operation is isolated in parseConfig. Everything after it handles typed data, and every result variant is covered.
When not to use it
Do not wrap every branch in a Result.
- Use a domain union such as
User | NotFoundwhen both variants are normal outcomes. - Let programmer errors fail loudly; they are bugs, not recoverable domain values.
- Use
unwraponly where crashing is the intended policy, such as required boot configuration.
Result describes expected failure. It is not a replacement for every union or every exception.
Related reference
attemptandattemptAsyncok,err,isOk, andisErrunwrapandtoErrorAbortErrorandTimeoutError- Pattern Matching