Concepts
Chaining results
Transform success and error payloads with map and mapErr while preserving the selected result branch.
On this page
Use map() to change a successful value without touching an error. Use mapErr() to change the error instead. Both return a result, so the caller still decides how to handle failure.
This function returns either a user or an error. map() trims a successful name, and mapErr() turns an error into a display message. The result type changes to Result<string, string> while preserving the original branch tags.
import { err, ok, type Result } from '@hulla/control'
function loadUser(): Result<{ name: string }, Error> {
return ok({ name: ' Sam ' })
}
const displayName = loadUser()
.map((user) => user.name.trim())
.mapErr((error) => `Could not load user: ${error.message}`)Only the callback for the active branch runs. If a mapper returns another Result, that value stays nested inside the outer branch.
Mappers may return promises. The result remains an Ok or Err containing that promise, so you can continue chaining; consume the eventual value with await on pair(), match(), or unwrap().
const doubled = ok(Promise.resolve(20))
.map((value) => Promise.resolve(value + 1))
.map((value) => value * 2)
const [value] = await doubled.pair()
console.log(value) // 42await doubled alone does not resolve the payload because the result object is not a promise. If the source promise rejects, or a mapper throws or rejects, that exception propagates; map() does not turn it into Err. Put an explicit exception boundary around work that should become a result with tcf().
Two promise shapes mean different things:
| Type | What to await |
|---|---|
Promise<Result<T, E>> | Await the operation, then narrow its result. |
Ok<Promise<T>> | Await .unwrap(), .match(), or .pair() to resolve the payload. |
The result wrapper isn’t a promise. See Async results for both forms.