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.

TypeScript
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.

map changes a payload, not the branch

There is no implicit flattening or flatMap(). When the next operation can fail, narrow the first result and return the second, as shown in the config checker.

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().

TypeScript
const doubled = ok(Promise.resolve(20))
  .map((value) => Promise.resolve(value + 1))
  .map((value) => value * 2)

const [value] = await doubled.pair()
console.log(value) // 42

await 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:

TypeWhat 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.