$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
expectedandgotproperties for detailed error info
✅ Signature
$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
| Name | Type | Description |
|---|---|---|
result | Result<T, E> | The Result value to manipulate |
🧩 API Methods
Type Checking
isOk: boolean
Returns true if the Result contains a success value.
const result = ok("success", $code.ok)
console.log($result(result).isOk) // true
const error = err("failure", $code.badRequest)
console.log($result(error).isOk) // falseisErr: boolean
Returns true if the Result contains an error value.
const result = ok("success", $code.ok)
console.log($result(result).isErr) // false
const error = err("failure", $code.badRequest)
console.log($result(error).isErr) // trueTransformation
map<U>(fn: (val: T) => U): Result<U, E>
Transforms the success value if the Result is Ok, otherwise returns the original error.
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) // truemapErr<F>(fn: (err: E) => F): Result<T, F>
Transforms the error value if the Result is Err, otherwise returns the original success.
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) // trueflatMap<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.
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) // trueExtraction
unwrap(): T
Extracts the success value or throws an error if the Result is Err.
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.
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.
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 originaltapErr(fn: (err: E) => void): Result<T, E>
Executes a side effect if the Result is Err, then returns the original Result.
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 originalPattern 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.
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:
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
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
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
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 nestedResult<Result<T, E>, E>types - Stack traces are automatically captured in development but omitted in production
- Use
tapOk()andtapErr()for logging and analytics without affecting the Result flow
🔗 Related
- Result Type - The underlying Result type definition
- $trap / $trapBag - Safe execution utilities
- $try / $atry - Try-catch Result wrappers
- $code - HTTP status codes for Results