Skip to content

$result()

$result() is a powerful utility for working with Result<T, E> types, providing a functional programming approach to error handling. It offers a comprehensive API for transforming, chaining, and extracting values from Result types with auto-inferred user messages and enhanced debugging context.

Functional result manipulation toolkit Transform, chain, and safely handle Result values with composable operations, auto-generated messages, and rich debugging info.

Overview

The $result() function wraps a Result<T, E> and provides methods for:

  • Transformation - map(), mapErr(), flatMap()
  • Extraction - unwrap(), unwrapOr()
  • Side effects - tapOk(), tapErr()
  • Pattern matching - match() with access to auto-generated messages
  • Type checking - isOk, isErr
  • Message access - Every Result includes user-friendly messages from $code
  • Debug context - Access to expected and got properties for detailed error info

✅ Signature

ts
$result<T, E>(result: Result<T, E>): {
  isOk: boolean
  isErr: boolean
  code: HTTPCode
  tapOk(fn: (val: T) => void): Result<T, E>
  tapErr(fn: (err: E) => void): Result<T, E>
  map<U>(fn: (val: T) => U): Result<U, E>
  mapErr<F>(fn: (err: E) => F): Result<T, F>
  flatMap<U>(fn: (val: T) => Result<U, E>): Result<U, E>
  unwrap(): T
  unwrapOr(fallback: T): T
  match<ROk, RErr>(cases: {
    ok: (val: Ok<T>) => ROk
    err: (val: Err<E>) => RErr
  }): ROk | RErr
  data: Result<T, E>
}

📥 Parameters

NameTypeDescription
resultResult<T, E>The Result value to manipulate

🧩 API Methods

Type Checking

isOk: boolean

Returns true if the Result contains a success value.

ts
const result = ok("success", $code.ok)
console.log($result(result).isOk) // true

const error = err("failure", $code.badRequest)
console.log($result(error).isOk) // false

isErr: boolean

Returns true if the Result contains an error value.

ts
const result = ok("success", $code.ok)
console.log($result(result).isErr) // false

const error = err("failure", $code.badRequest)
console.log($result(error).isErr) // true

Transformation

map<U>(fn: (val: T) => U): Result<U, E>

Transforms the success value if the Result is Ok, otherwise returns the original error.

ts
const result = ok(10, $code.ok)
const doubled = $result(result).map(x => x * 2)

if (doubled.ok) {
  console.log(doubled.data) // 20
}

// Errors pass through unchanged
const error = err("fail", $code.badRequest)
const unchanged = $result(error).map(x => x * 2)
console.log(unchanged === error) // true

mapErr<F>(fn: (err: E) => F): Result<T, F>

Transforms the error value if the Result is Err, otherwise returns the original success.

ts
const error = err("network timeout", $code.gatewayTimeout)
const transformed = $result(error).mapErr(err => `Error: ${err}`)

if (!transformed.ok) {
  console.log(transformed.error) // "Error: network timeout"
}

// Success values pass through unchanged
const success = ok("data", $code.ok)
const unchanged = $result(success).mapErr(err => `Error: ${err}`)
console.log(unchanged === success) // true

flatMap<U>(fn: (val: T) => Result<U, E>): Result<U, E>

Chains Results together, avoiding nested Result types. Only calls the function if the Result is Ok.

ts
const parseNumber = (str: string): Result<number, string> => {
  const num = parseInt(str)
  return isNaN(num) ? err("Invalid number", $code.badRequest) : ok(num, $code.ok)
}

const result = ok("42", $code.ok)
const parsed = $result(result).flatMap(parseNumber)

if (parsed.ok) {
  console.log(parsed.data) // 42
}

// Error short-circuits the chain
const error = err("no input", $code.badRequest)
const notCalled = $result(error).flatMap(parseNumber)
console.log(notCalled === error) // true

Extraction

unwrap(): T

Extracts the success value or throws an error if the Result is Err.

ts
const success = ok("data", $code.ok)
console.log($result(success).unwrap()) // "data"

const error = err("failure", $code.badRequest)
$result(error).unwrap() // throws: "Tried to unwrap Err: failure"

unwrapOr(fallback: T): T

Extracts the success value or returns the fallback if the Result is Err.

ts
const success = ok("data", $code.ok)
console.log($result(success).unwrapOr("default")) // "data"

const error = err("failure", $code.badRequest)
console.log($result(error).unwrapOr("default")) // "default"

Side Effects

tapOk(fn: (val: T) => void): Result<T, E>

Executes a side effect if the Result is Ok, then returns the original Result.

ts
const result = ok("success", $code.ok)
const logged = $result(result).tapOk(data => {
  console.log("Success:", data) // logs: "Success: success"
})

console.log(logged === result) // true - returns original

tapErr(fn: (err: E) => void): Result<T, E>

Executes a side effect if the Result is Err, then returns the original Result.

ts
const error = err("failure", $code.badRequest)
const logged = $result(error).tapErr(err => {
  console.error("Error:", err) // logs: "Error: failure"
})

console.log(logged === error) // true - returns original

Pattern Matching

match<ROk, RErr>(cases: { ok: (val: Ok<T>) => ROk, err: (val: Err<E>) => RErr }): ROk | RErr

Provides exhaustive pattern matching over the Result with access to auto-generated messages and debugging context.

ts
const result = ok("data", $code.ok)

const message = $result(result).match({
  ok: (val) => `Success: ${val.data} (${val.message})`,
  err: (err) => `Error: ${err.error} (${err.message})`
})

console.log(message) // "Success: data (Request succeeded.)"

// With debugging context
const validationResult = err("Invalid input", $code.validationTypeError, {
  expected: { name: "string" },
  got: { name: 123 }
})

$result(validationResult).match({
  ok: (val) => handleSuccess(val.data),
  err: (err) => {
    console.log("User message:", err.message)  // "Invalid data type provided."
    console.log("Technical error:", err.error) // "Invalid input"
    console.log("Expected:", err.expected)     // { name: "string" }
    console.log("Got:", err.got)              // { name: 123 }
  }
})

🔄 Chaining Operations

$result() methods can be chained together for powerful data transformation pipelines:

ts
const processUser = (input: string) => {
  return $result(parseJSON(input))
    .flatMap(validateUser)
    .map(normalizeUser)
    .tapOk(user => console.log("Processed:", user.name))
    .tapErr(error => console.error("Failed:", error))
    .unwrapOr(defaultUser)
}

🎯 Real-World Examples

API Response Processing

ts
async function fetchUserProfile(id: string) {
  const response = await $atry(() => fetch(`/api/users/${id}`))
  
  return $result(response)
    .flatMap(parseResponse)
    .map(user => ({
      ...user,
      displayName: `${user.firstName} ${user.lastName}`
    }))
    .tapErr(error => {
      console.error("Failed to fetch user:", error)
      analytics.track("user_fetch_error", { userId: id, error })
    })
}

// Usage
const user = await fetchUserProfile("123")
const profile = $result(user).match({
  ok: (user) => renderProfile(user.data),
  err: (error) => renderErrorState(error.error)
})

Form Validation Pipeline

ts
function validateRegistration(data: any) {
  return $result(validateEmail(data.email))
    .flatMap(() => validatePassword(data.password))
    .flatMap(() => validateAge(data.age))
    .map(() => ({
      email: data.email.toLowerCase(),
      passwordHash: hashPassword(data.password),
      age: parseInt(data.age)
    }))
    .tapOk(user => console.log("Valid registration:", user.email))
    .tapErr(errors => console.log("Validation failed:", errors))
}

Error Recovery

ts
function fetchWithFallback(primaryUrl: string, fallbackUrl: string) {
  return $result(primaryRequest)
    .match({
      ok: (data) => data,
      err: () => {
        console.log("Primary failed, trying fallback...")
        return $result(fallbackRequest).unwrapOr(defaultData)
      }
    })
}

🧠 Notes

  • All $result() operations are pure functions - they don't mutate the original Result
  • Error types are preserved through transformations unless explicitly changed with mapErr()
  • The flatMap() method prevents nested Result<Result<T, E>, E> types
  • Stack traces are automatically captured in development but omitted in production
  • Use tapOk() and tapErr() for logging and analytics without affecting the Result flow