receta

Result

27 exports from @ontologyio/receta/result.

Everything exported from @ontologyio/receta/result. New here? Start with the Result guide.

Functions

collect

Collects an array of Results into a single Result of an array.

If all Results are Ok, returns Ok with an array of all values. If any Result is Err, returns the first Err encountered.

This is useful for validating multiple independent operations and collecting all successes or short-circuiting on the first error.

In a pipe, write collect(). A bare collect runs correctly but loses the element type.

Data-last
function collect<T>(): <E>(results: readonly Result<T, E>[]) => Result<T[], E>
Data-first
function collect<T, E>(results: readonly Result<T, E>[]): Result<T[], E>
Example
// All successful
collect([ok(1), ok(2), ok(3)])
// => Ok([1, 2, 3])

// First error short-circuits
collect([ok(1), err('fail'), ok(3)])
// => Err('fail')

// Practical use case
const validateUser = (data: UserInput) => pipe(
  [
    validateName(data.name),
    validateEmail(data.email),
    validateAge(data.age)
  ],
  collect(),
  map(([name, email, age]) => ({ name, email, age }))
)

See also partition.

err

Creates a failed Result containing an error.

Signature
function err<E>(error: E): Err<E>
Example
const result = err('Something went wrong')
// => { _tag: 'Err', error: 'Something went wrong' }

flatMap

Chains Result-returning operations (monadic bind).

If the Result is Ok, applies the function to its value and returns the resulting Result. If the Result is Err, returns the Err unchanged.

Use this when you have a sequence of operations that each return a Result. This prevents nesting Results (Result<Result<T, E>, E>).

Data-first
function flatMap<T, U, E, F>(result: Result<T, E>, fn: (value: T) => Result<U, F>): Result<U, E | F>
Data-last
function flatMap<T, U, F>(fn: (value: T) => Result<U, F>): <E>(result: Result<T, E>) => Result<U, F | E>
Example
// Data-first
const parseNumber = (str: string): Result<number, string> =>
  str === '' ? err('Empty string') : ok(Number(str))

flatMap(ok('42'), parseNumber) // => Ok(42)
flatMap(ok(''), parseNumber) // => Err('Empty string')
flatMap(err('fail'), parseNumber) // => Err('fail')

// Data-last (in pipe) - chaining operations
pipe(
  tryCatch(() => readFile('config.json')),
  flatMap(str => tryCatch(() => JSON.parse(str))),
  flatMap(config => validateConfig(config)),
  map(config => config.port)
)

See also map, flatten.

flatten

Flattens a nested Result one level.

Converts Result<Result<T, E>, E> to Result<T, E>.

In a pipe, write flatten(). A bare flatten runs correctly but loses the element type.

Data-last
function flatten<T, E>(): <F>(result: Result<Result<T, E>, F>) => Result<T, E | F>
Data-first
function flatten<T, E, F>(result: Result<Result<T, E>, F>): Result<T, E | F>
Example
// Data-first
flatten(ok(ok(42))) // => Ok(42)
flatten(ok(err('inner'))) // => Err('inner')
flatten(err('outer')) // => Err('outer')

// Data-last (in pipe)
pipe(
  ok(ok(42)),
  flatten()
) // => Ok(42)

See also flatMap.

fromNullable

Converts a nullable value to a Result.

If the value is not null or undefined, returns Ok with the value. Otherwise, returns Err with the provided error.

Data-first
function fromNullable<T, E>(value: T | null | undefined, error: E): Result<T, E>
Data-last
function fromNullable<E>(error: E): <T>(value: T | null | undefined) => Result<T, E>
Example
// Data-first
fromNullable(42, 'No value') // => Ok(42)
fromNullable(null, 'No value') // => Err('No value')
fromNullable(undefined, 'No value') // => Err('No value')

// Data-last (in pipe)
pipe(
  users.find(u => u.id === id),
  fromNullable('User not found'),
  map(user => user.name)
)

// With a config lookup
pipe(
  config.get('apiKey'),
  fromNullable('Missing API key in config')
)

isErr

Type guard to check if a Result is Err.

Narrows the type to Err<E>, allowing safe access to the error property.

Signature
function isErr<T, E>(result: Result<T, E>): result is Err<E>
Example
const result: Result<number, string> = err('Failed')

if (isErr(result)) {
  console.log(result.error) // TypeScript knows this is safe
}

isOk

Type guard to check if a Result is Ok.

Narrows the type to Ok<T>, allowing safe access to the value property.

Signature
function isOk<T, E>(result: Result<T, E>): result is Ok<T>
Example
const result: Result<number, string> = ok(42)

if (isOk(result)) {
  console.log(result.value) // TypeScript knows this is safe
}

map

Maps over the Ok value of a Result.

If the Result is Ok, applies the function to its value and returns a new Ok. If the Result is Err, returns the Err unchanged.

This is the functor map operation for Result.

Data-first
function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>
Data-last
function map<T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>
Example
// Data-first
map(ok(5), x => x * 2) // => Ok(10)
map(err('fail'), (x: number) => x * 2) // => Err('fail')

// Data-last (in pipe)
pipe(
  ok(5),
  map(x => x * 2),
  map(x => x + 1)
) // => Ok(11)

See also mapErr, flatMap.

mapErr

Maps over the Err value of a Result.

If the Result is Err, applies the function to its error and returns a new Err. If the Result is Ok, returns the Ok unchanged.

Useful for transforming or enriching error values.

Data-first
function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>
Data-last
function mapErr<E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>
Example
// Data-first
mapErr(err('fail'), e => `Error: ${e}`) // => Err('Error: fail')
mapErr(ok(5), e => `Error: ${e}`) // => Ok(5)

// Data-last (in pipe)
pipe(
  parseJSON(str),
  mapErr(e => ({
    code: 'PARSE_ERROR',
    message: e.message,
    timestamp: Date.now()
  }))
)

See also map.

match

Pattern matches on a Result, providing handlers for both cases.

This is a more functional alternative to if/else or switch statements. Both handlers must return the same type.

Data-first
function match<T, E, U>(result: Result<T, E>, handlers: { readonly onOk: (value: T) => U; readonly onErr: (error: E) => U; }): U
Data-last
function match<T, E, U>(handlers: { readonly onOk: (value: T) => U; readonly onErr: (error: E) => U; }): (result: Result<T, E>) => U
Example
// Data-first
match(
  parseNumber('42'),
  {
    onOk: n => `Success: ${n}`,
    onErr: e => `Error: ${e}`
  }
) // => 'Success: 42'

// Data-last (in pipe)
pipe(
  fetchUser(id),
  match({
    onOk: user => renderUser(user),
    onErr: error => renderError(error)
  })
)

// With different handler types
const getStatus = match({
  onOk: () => 'success' as const,
  onErr: () => 'error' as const
})

ok

Creates a successful Result containing a value.

Signature
function ok<T>(value: T): Ok<T>
Example
const result = ok(42)
// => { _tag: 'Ok', value: 42 }

orThrow

Converts a Result-returning async function into a throwing variant.

This is a higher-order function that wraps any async function returning Promise<Result<T, E>> to instead return Promise<T> and throw on errors.

Use this to create *OrThrow variants without duplicating logic.

Pass a function with a single signature: an overloaded function such as mapAsync types as never, so pass its curried form instead.

Signature
function orThrow<F extends (...args: any[]) => Promise<Result<any, any>>>(fn: F): OrThrowVersion<F>
Example
import { mapAsync } from '@ontologyio/receta/async'
import { orThrow } from '@ontologyio/receta/result'

// Create throwing variant
const fetchAllOrThrow = orThrow(
  mapAsync(async (url: string) => fetch(url))
)

// Use it
try {
  const results = await fetchAllOrThrow(urls)
  console.log(results)
} catch (error) {
  console.error('Failed:', error)
}

See also unwrap.

parseInt

Safely parses a string into an integer with optional radix.

Unlike the global parseInt() which can return NaN or parse partial strings, this function returns a Result with strict validation:

  • Must be a valid integer
  • No trailing non-numeric characters allowed
  • Radix must be between 2 and 36
Signature
function parseInt(str: string, radix?: number): Result<number, ParseNumberError>

Fails with ParseNumberError.

Example
import { parseInt } from '@ontologyio/receta/result'

// Decimal (default radix 10)
parseInt('123')
// => Ok(123)

parseInt('-42')
// => Ok(-42)

parseInt('0')
// => Ok(0)

// Binary (radix 2)
parseInt('1010', 2)
// => Ok(10)

parseInt('11111111', 2)
// => Ok(255)

// Hexadecimal (radix 16)
parseInt('FF', 16)
// => Ok(255)

parseInt('0x1A', 16)
// => Ok(26)

// Octal (radix 8)
parseInt('77', 8)
// => Ok(63)

// Failed parsing - not a number
parseInt('abc')
// Err with reason 'not_a_number'

// Failed parsing - has decimal point
parseInt('123.45')
// Err with reason 'invalid_integer'

// Failed parsing - invalid radix
parseInt('123', 1)
// Err with reason 'out_of_radix_range'

parseInt('123', 37)
// Err with reason 'out_of_radix_range'

// Use with pattern matching
pipe(
  parseInt('FF', 16),
  match({
    onOk: (n) => `Decimal: ${n}`,
    onErr: (e) => `Error: ${e.message}`
  })
)
// => 'Decimal: 255'

See also parseNumber.

parseJSON

Safely parses a JSON string into a typed value.

Unlike JSON.parse which throws on invalid input, this function returns a Result that either contains the parsed value or a SyntaxError.

Signature
function parseJSON<T = unknown>(str: string): Result<T, SyntaxError>

Fails with SyntaxError.

Example
// Successful parsing
parseJSON('{"name":"John"}')
// => Ok({ name: 'John' })

parseJSON('[1, 2, 3]')
// => Ok([1, 2, 3])

// Failed parsing
parseJSON('invalid json')
// Err(SyntaxError)

parseJSON('{"incomplete":')
// Err(SyntaxError)

// With type annotation
interface User {
  name: string
  age: number
}

const result = parseJSON<User>('{"name":"Alice","age":30}')
// => Ok({ name: 'Alice', age: 30 })

// Use with pipe and map
pipe(
  parseJSON<User>('{"name":"Bob","age":25}'),
  map(user => user.name.toUpperCase())
)
// => Ok('BOB')

parseNumber

Safely parses a string into a number.

Unlike Number() which can produce NaN or Infinity, this function returns a Result that either contains a valid finite number or a descriptive error.

Signature
function parseNumber(str: string): Result<number, ParseNumberError>

Fails with ParseNumberError.

Example
// Successful parsing
parseNumber('123')
// => Ok(123)

parseNumber('3.14')
// => Ok(3.14)

parseNumber('-42.5')
// => Ok(-42.5)

parseNumber('0')
// => Ok(0)

// Scientific notation
parseNumber('1.5e10')
// => Ok(15000000000)

// Failed parsing - not a number
parseNumber('abc')
// Err with reason 'not_a_number'

parseNumber('')
// Err with reason 'not_a_number'

// Failed parsing - infinite
parseNumber('Infinity')
// Err with reason 'infinite'

parseNumber('-Infinity')
// Err with reason 'infinite'

// Use in validation pipelines
pipe(
  parseNumber('42'),
  flatMap(n => n > 0 ? ok(n) : err('Must be positive'))
)
// => Ok(42)

See also parseInt.

partition

Separates an array of Results into Ok values and Err values.

Returns a tuple of [okValues, errValues].

In a pipe, write partition(). A bare partition runs correctly but loses the element type.

Data-last
function partition<T>(): <E>(results: readonly Result<T, E>[]) => [T[], E[]]
Data-first
function partition<T, E>(results: readonly Result<T, E>[]): [T[], E[]]
Example
const results = [
  ok(1),
  err('fail1'),
  ok(2),
  err('fail2'),
  ok(3)
]

partition(results)
// => [[1, 2, 3], ['fail1', 'fail2']]

// Practical use case: bulk validation
const [validUsers, errors] = pipe(
  rawUsers,
  R.map(validateUser),
  partition()
)

if (errors.length > 0) {
  console.log('Validation errors:', errors)
}
console.log('Valid users:', validUsers)

See also collect.

tap

Performs a side effect with the Ok value without modifying the Result.

Useful for logging, debugging, or performing side effects in a pipeline while keeping the Result unchanged.

Data-first
function tap<T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>
Data-last
function tap<T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>
Example
// Data-first
tap(ok(42), n => console.log('Got:', n)) // logs 'Got: 42', returns Ok(42)
tap(err('fail'), n => console.log(n)) // no log, returns Err('fail')

// Data-last (in pipe) - debugging
pipe(
  fetchUser(id),
  tap(user => console.log('Fetched user:', user.name)),
  map(user => user.email),
  tap(email => console.log('Email:', email))
)

See also tapErr.

tapErr

Performs a side effect with the Err value without modifying the Result.

Useful for error logging or monitoring while keeping the error flowing through the pipeline.

Data-first
function tapErr<T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>
Data-last
function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>
Example
// Data-first
tapErr(ok(42), e => console.error(e)) // no log, returns Ok(42)
tapErr(err('fail'), e => console.error('Error:', e)) // logs, returns Err('fail')

// Data-last (in pipe) - error monitoring
pipe(
  parseConfig(str),
  tapErr(error => {
    logError(error)
    sendToMonitoring(error)
  }),
  mapErr(error => 'Using default config due to error'),
  unwrapOr(defaultConfig)
)

See also tap.

tryCatch

Wraps a potentially throwing function in a Result.

If the function executes successfully, returns Ok with the value. If it throws, returns Err with the error. Wraps a potentially throwing function in a Result with custom error mapping.

Overload 1
function tryCatch<T>(fn: () => T): Result<T, unknown>
Overload 2
function tryCatch<T, E>(fn: () => T, mapError: (error: unknown) => E): Result<T, E>
Example
const parseJSON = (str: string) =>
  tryCatch(() => JSON.parse(str))

parseJSON('{"name":"John"}')
// => Ok({ name: 'John' })

parseJSON('invalid json')
// Err(SyntaxError)
Example
const parseNumber = (str: string) =>
  tryCatch(
    () => {
      const n = Number(str)
      if (Number.isNaN(n)) throw new Error('Invalid number')
      return n
    },
    (e) => `Parse error: ${e}`
  )

tryCatchAsync

Wraps an async function in a Result, catching any errors. Wraps an async function in a Result with custom error mapping.

Overload 1
function tryCatchAsync<T>(fn: () => Promise<T>): Promise<Result<T, unknown>>
Overload 2
function tryCatchAsync<T, E>(fn: () => Promise<T>, mapError: (error: unknown) => E): Promise<Result<T, E>>
Example
const fetchUser = async (id: string) =>
  tryCatchAsync(async () => {
    const res = await fetch(`/api/users/${id}`)
    return res.json()
  })

const result = await fetchUser('123')
// Ok({ name: 'John' }) or Err(NetworkError)
Example
const fetchUser = async (id: string) =>
  tryCatchAsync(
    async () => {
      const res = await fetch(`/api/users/${id}`)
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      return res.json()
    },
    (e) => ({ code: 'FETCH_ERROR', message: String(e) })
  )

unwrap

Extracts the value from an Ok Result or throws the error.

Warning: This function throws if the Result is Err. Use unwrapOr or unwrapOrElse for safer alternatives.

Signature
function unwrap<T, E>(result: Result<T, E>): T
Example
unwrap(ok(42)) // => 42
unwrap(err('fail')) // throws 'fail'

See also unwrapOr, unwrapOrElse.

unwrapOr

Extracts the value from an Ok Result or returns a default.

Data-first
function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T
Data-last
function unwrapOr<T>(defaultValue: T): <E>(result: Result<T, E>) => T
Example
// Data-first
unwrapOr(ok(42), 0) // => 42
unwrapOr(err('fail'), 0) // => 0

// Data-last (in pipe)
pipe(
  parseNumber('abc'),
  unwrapOr(0)
) // => 0

See also unwrapOrElse.

unwrapOrElse

Extracts the value from an Ok Result or computes a fallback from the error.

Unlike unwrapOr, this allows you to compute the default value lazily and access the error for decision-making.

Data-first
function unwrapOrElse<T, E>(result: Result<T, E>, fn: (error: E) => T): T
Data-last
function unwrapOrElse<T, E>(fn: (error: E) => T): (result: Result<T, E>) => T
Example
// Data-first
unwrapOrElse(ok(42), () => 0) // => 42
unwrapOrElse(err('fail'), e => {
  console.error(e)
  return 0
}) // logs 'fail', returns 0

// Data-last (in pipe)
pipe(
  fetchUser(id),
  unwrapOrElse(error => {
    logError(error)
    return guestUser
  })
)

See also unwrapOr.

Types

Err

Represents a failed computation containing an error.

Signature
interface Err<E> {
  readonly _tag: 'Err'
  readonly error: E
}

Ok

Represents a successful computation containing a value.

Signature
interface Ok<T> {
  readonly _tag: 'Ok'
  readonly value: T
}

ParseNumberError

Error returned when number parsing fails.

Signature
interface ParseNumberError {
  readonly _tag: 'ParseNumberError'
  readonly input: string
  readonly reason: 'not_a_number' | 'infinite' | 'invalid_integer' | 'out_of_radix_range'
  readonly message: string
}

Result

Result type representing either success (Ok) or failure (Err).

Signature
type Result<T, E> = Ok<T> | Err<E>
Example
type ParseResult = Result<number, string>

const success: ParseResult = { _tag: 'Ok', value: 42 }
const failure: ParseResult = { _tag: 'Err', error: 'Invalid number' }

On this page