Result
Result type is really useful to describe the result of a certain operation without relying on exceptions.
type Result<A, B> = Ok<A> | Error<B>
catchError
Returns mapFn(err) when result is of the form Error(err), otherwise, returns result unchanged.
function catchError<A, B, C>(result: Result<A, B>, mapFn: (err: B) => Result<A, C>): Result<A, C>
function catchError<A, B, C>(mapFn: (err: B) => Result<A, C>): (result: Result<A, B>) => Result<A, C>
// type User = { name: string, age: number } | null
pipe(
  // ⬇️ const obj: User = { name: 'Joe', age: 20 }
  R.fromNullable(obj, 'unknown'),
  R.catchError(err => {
    return R.Ok({
      name: err,
      age: 0,
    })
  }),
  R.map(obj => obj.name),
) // → Ok('Joe')
pipe(
  // ⬇️ const obj: User = null
  R.fromNullable(obj, 'unknown'),
  R.catchError(err => {
    return R.Error(`${err} value`)
  }),
  R.map(obj => obj.name),
) // → Error('unknown value')
flatMap
Returns the result of mapFn (it must have a return type of Result) if result is Ok(value), otherwise, returns result unchanged.
function flatMap<A, B, C>(result: Result<A, B>, mapFn: (value: A) => Result<C, B>): Result<C, B>
function flatMap<A, B, C>(mapFn: (value: A) => Result<C, B>): (result: Result<A, B>) => Result<C, B>
pipe(
  R.fromNullable(4, 'value cannot be nullable'),
  R.flatMap(value => {
    return value === 0
      ? R.Error('never divide by zero')
      : R.Ok(100 / value)
  }),
) // → Ok(25)
pipe(
  R.fromNullable(null, 'value cannot be nullable'),
  R.flatMap(value => {
    return value === 0
      ? R.Error('never divide by zero')
      : R.Ok(100 / value)
  }),
) // → Error('value cannot be nullable')
pipe(
  R.fromNullable(0, 'value cannot be nullable'),
  R.flatMap(value => {
    return value === 0
      ? R.Error('never divide by zero')
      : R.Ok(100 / value)
  }),
) // → Error('never divide by zero')
flip
Swaps the values between the Ok and Error.
function flip<A, B>(result: Result<A, B>): Result<B, A>
// type Value = string | null
pipe(
  // ⬇️ const value: Value = 'Joe'
  R.fromNullable(value, 'value cannot be nullable'),
  R.flip,
) // → Error('Joe')
// type Value = string | null
pipe(
  // ⬇️ const value: Value = null
  R.fromNullable(value, 'value cannot be nullable'),
  R.flip,
) // → Ok('value cannot be nullable')
fromExecution
Returns Ok(value) (value is the result of fn) if fn didn't throw an error, otherwise, returns Error(err).
function fromExecution<A>(fn: () => A): Result<ExtractValue<A>, globalThis.Error>
type User = { readonly name: string }
const parseJSON = <T>(value: string) => () => JSON.parse(value) as T
const okJSON = `{"name": "John"}`
const badJSON = `{name": John}`
R.fromExecution(parseJSON<User>(okJSON)) // → Ok({ name: 'John' })
R.fromExecution(parseJSON<User>(badJSON)) // → Error(new SyntaxError('Unexpected token n in JSON at position 1'))
fromFalsy
Returns Ok(value) if value is not falsy, otherwise, returns Error(errorValue).
function fromFalsy<A, B>(value: A, errorValue: NonNullable<B>): Result<ExtractValue<A>, B>
function fromFalsy<A, B>(errorValue: NonNullable<B>): (value: A) => Result<ExtractValue<A>, B>
R.fromFalsy(4, 'falsy') // → Ok(4)
R.fromFalsy([], 'falsy') // → Ok([])
R.fromFalsy({}, 'falsy') // → Ok({})
R.fromFalsy(0, 'falsy') // → Error('falsy')
R.fromFalsy('', 'falsy') // → Error('falsy')
R.fromFalsy(false, 'falsy') // → Error('falsy')
fromNullable
Returns Ok(value) if value is non-nullable, otherwise, returns Error(errorValue).
function fromNullable<A, B>(value: A, errorValue: NonNullable<B>): Result<ExtractValue<A>, B>
function fromNullable<A, B>(errorValue: NonNullable<B>): (value: A) => Result<ExtractValue<A>, B>
R.fromNullable(null, 'nullable') // → Error('nullable')
R.fromNullable(undefined, 'nullable') // → Error('nullable')
R.fromNullable([], 'nullable') // → Ok([])
R.fromNullable({}, 'nullable') // → Ok({})
R.fromNullable(0, 'nullable') // → Ok(0)
R.fromNullable(1, 'nullable') // → Ok(1)
R.fromNullable(false, 'nullable') // → Ok(false)
fromPredicate
Returns Ok(value) if the predicate function returns true, otherwise, returns Error(errorValue).
function fromPredicate<A, B>(
    value: A,
    predicateFn: (value: NonNullable<A>) => boolean,
    errorValue: NonNullable<B>
): Result<ExtractValue<A>, B>
function fromPredicate<A, B>(
    predicateFn: (value: NonNullable<A>) => boolean,
    errorValue: NonNullable<B>
): (value: A) => Result<ExtractValue<A>, B>
R.fromPredicate(
  [1, 2, 3],
  A.some(x => x === 2),
  'value not found',
) // → Ok([1, 2, 3])
R.fromPredicate(
  [1, 2, 3],
  A.some(x => x === 4),
  'value not found',
) // → Error('value not found')
fromPromise
Returns Ok(value) if promise is resolved successfully, otherwise, returns Error(err).
function fromPromise<A>(promise: Promise<A>): Promise<Result<ExtractValue<A>, globalThis.Error>>
await R.fromPromise(Promise.resolve('hello world')) // → Ok('hello world')
await R.fromPromise(Promise.reject('oops')) // → Error('oops')
getExn
Returns value if result is Ok(value), otherwise, throws an exception.
function getExn<A, B>(result: Result<A, B>): A | never
pipe(
  R.fromNullable('hello', 'oops!'),
  R.map(value => `${value} world!`),
  R.getExn,
) // → 'hello world!'
getWithDefault
Returns value if result is Ok(value), otherwise, returns a default value.
function getWithDefault<A, B>(result: Result<A, B>, defaultValue: NonNullable<A>): A
function getWithDefault<A, B>(defaultValue: NonNullable<A>): (result: Result<A, B>) => A
pipe(
  R.fromNullable('hello', 'oops!'),
  R.map(value => `${value} world!`),
  R.getWithDefault('error'),
) // → 'hello world!'
pipe(
  R.fromNullable(null, 'oops!'),
  R.map(value => `${value} world!`),
  R.getWithDefault('error'),
) // → 'error'
handleError
Converts errors into successful values, and returns a Result where the error channel is voided, to indicate that the error has been handled.
function handleError<A, B>(result: Result<A, B>, mapFn: (err: B) => NonNullable<A>): Result<A, void>
function handleError<A, B>(mapFn: (err: B) => NonNullable<A>): (result: Result<A, B>) => Result<A, void>
// type User = { name: string, age: number } | null
pipe(
  // ⬇️ const obj: User = { name: 'Joe', age: 20 }
  R.fromNullable(obj, 'unknown'),
  R.handleError(err => {
    return {
      name: err,
      age: 0,
    }
  }),
  R.map(obj => obj.name),
) // → Ok('Joe')
pipe(
  // ⬇️ const obj: User = null
  R.fromNullable(obj, 'unknown'),
  R.handleError(err => {
    return {
      name: err,
      age: 0,
    }
  }),
  R.map(obj => obj.name),
) // → Ok('unknown')
isError
Returns true if the provided result is Error(errorValue), otherwise, returns false.
function isError<A, B>(result: Result<A, B>): result is Error<B>
R.isError(R.Error('bad')) // → true
pipe(R.fromNullable(null, 'error'), R.isError) // → true
R.isError(R.Ok('good')) // → false
pipe(R.fromNullable(4, 'error'), R.isError) // → false
isOk
Returns true if the provided result is Ok(value), otherwise, returns false.
function isOk<A, B>(result: Result<A, B>): result is Ok<A>
R.isOk(R.Ok('good')) // → true
pipe(R.fromNullable(4, 'error'), R.isOk) // → true
R.isOk(R.Error('bad')) // → false
pipe(R.fromNullable(null, 'error'), R.isOk) // → false
map
Returns the result of mapFn if result is Ok(value), otherwise, returns Error(errorValue) and mapFn is not called.
function map<A, B, R>(result: Result<A, B>, mapFn: (value: A) => NonNullable<R>): Result<R, B>
function map<A, B, R>(mapFn: (value: A) => NonNullable<R>): (result: Result<A, B>) => Result<R, B>
// type User = { name: string | null, age: number } | null
pipe(
  // ⬇️ const obj: User = { name: 'Joe', age: 20 }
  R.fromNullable(obj, 'cannot be nullable'),
  R.flatMap(obj => {
    return G.isNotNullable(obj.name) ? R.Ok(obj) : R.Error('missing name')
  }),
  R.map(obj => `${obj.name} is ${obj.age} year old!`),
) // → Ok('Joe is 20 year old!')
pipe(
  // ⬇️ const obj: User = { name: null, age: 20 }
  R.fromNullable(obj, 'cannot be nullable'),
  R.flatMap(obj => {
    return G.isNotNullable(obj.name) ? R.Ok(obj) : R.Error('missing name')
  }),
  R.map(obj => `${obj.name} is ${obj.age} year old!`),
) // → Error('missing name')
pipe(
  // ⬇️ const obj: User = null
  R.fromNullable(obj, 'cannot be nullable'),
  R.flatMap(obj => {
    return G.isNotNullable(obj.name) ? R.Ok(obj) : R.Error('missing name')
  }),
  R.map(obj => `${obj.name} is ${obj.age} year old!`),
) // → Error('cannot be nullable')
mapError
Returns result unchanged if result is of the form Ok, otherwise, returns Error(mapFn(err)).
function mapError<A, B, C>(result: Result<A, B>, mapFn: (err: B) => NonNullable<C>): Result<A, C>
function mapError<A, B, C>(mapFn: (err: B) => NonNullable<C>): (result: Result<A, B>) => Result<A, C>
// type User = { name: string, age: number } | null
pipe(
  // ⬇️ const obj: User = { name: 'Joe', age: 20 }
  R.fromNullable(obj, 'unknown'),
  R.mapError(err => {
    return `${err} value`
  }),
  R.map(obj => obj.name),
) // → Ok('Joe')
pipe(
  // ⬇️ const obj: User = null
  R.fromNullable(obj, 'unknown'),
  R.mapError(err => {
    return `${err} value`
  }),
  R.map(obj => obj.name),
) // → Error('unknown value')
mapWithDefault
Returns the result of mapFn if result is Ok(value), otherwise returns a default value.
function mapWithDefault<A, B, R>(
    result: Result<A, B>,
    defaultValue: NonNullable<R>,
    mapFn: (value: A) => NonNullable<R>
): R
function mapWithDefault<A, B, R>(defaultValue: NonNullable<R>, mapFn: (value: A) => NonNullable<R>): (result: Result<A, B>) => R
// type Name = string | null
pipe(
  // ⬇️ const name: Name = 'Joe'
  R.fromNullable(name, 'cannot be nullable'),
  R.mapWithDefault('Hello, stranger!', name => `Hello, ${name}!`),
) // → 'Hello, Joe!'
pipe(
  // ⬇️ const name: Name = null
  R.fromNullable(name, 'cannot be nullable'),
  R.mapWithDefault('Hello, stranger!', name => `Hello, ${name}!`),
) // → 'Hello, stranger!'
match
Returns the result of okFn if result is Ok(value), otherwise, returns the result of errorFn.
function match<A, B, R>(result: Result<A, B>, okFn: (value: A) => R, errorFn: (value: B) => R): R
function match<A, B, R>(okFn: (value: A) => R, errorFn: (value: B) => R): (result: Result<A, B>) => R
// type Elements = ReadonlyArray<string> | null
pipe(
  // ⬇️ const xs: Elements = ['hello', 'world']
  R.fromNullable(xs, 'cannot be nullable'),
  R.map(A.join(' ')),
  R.match(
    str => `${str}!`,
    () => 'oops!',
  ),
) // → 'hello world!'
pipe(
  // ⬇️ const xs: Elements = null
  R.fromNullable(xs, 'cannot be nullable'),
  R.map(A.join(' ')),
  R.match(
    str => `${str}!`,
    error => `${error}!`,
  ),
) // → 'cannot be nullable!'
recover
Ensures that the returned result is Ok by returning the provided result if it's already [Ok], or by falling back to the default value, which will be wrapped in the Ok constructor, if the provided result is an Error.
function recover<A, B>(result: Result<A, B>, defaultValue: NonNullable<A>): Result<A, B>
function recover<A, B>(defaultValue: NonNullable<A>): (result: Result<A, B>) => Result<A, B>
// type User = { name: string | null, age: number } | null
pipe(
  // ⬇️ const obj: User = { name: 'Joe', age: 20 }
  R.fromNullable(obj, 'value cannot be nullable'),
  R.flatMap(obj => {
    return obj.name ? R.Ok(obj.name) : R.Error('name not found')
  }),
  R.recover('unknown name'),
) // → Ok('Joe')
pipe(
  // ⬇️ const obj: User = { name: null, age: 20 }
  R.fromNullable(obj, 'value cannot be nullable'),
  R.flatMap(obj => {
    return obj.name ? R.Ok(obj.name) : R.Error('name not found')
  }),
  R.recover('unknown name'),
) // → Ok('unknown name')
tap
Applies a side-effect function to the value in Ok, and returns the original result.
function tap<A, B>(result: Result<A, B>, okFn: (value: A) => void): Result<A, B>
function tap<A, B>(okFn: (value: A) => void): (result: Result<A, B>) => Result<A, B>
pipe(
  R.fromNullable('hello', 'value cannot be nullable'),
  R.map(S.isEmpty),
  R.tap(isEmpty => {
    console.log(isEmpty) // ⬅️ false
  }),
  R.getWithDefault(false),
) // → false
tapError
Applies a side-effect function to the value in Error, and returns the original result.
function tapError<A, B>(result: Result<A, B>, errorFn: (err: B) => void): Result<A, B>
function tapError<A, B>(errorFn: (err: B) => void): (result: Result<A, B>) => Result<A, B>
pipe(
  R.fromNullable(null, 'value cannot be nullable'),
  R.tapError(err => {
    console.log(err) // ⬅️ 'value cannot be nullable'
  }),
  R.getWithDefault(false),
) // → false
toNullable
Returns value if result is Ok(value), otherwise, returns null.
function toNullable<A, B>(result: Result<A, B>): A | null
pipe(
  R.fromNullable(['hello', 'world', 'ts', 'belt'], 'cannot be nullable'),
  R.flatMap(xs => {
    return pipe(xs, A.dropExactly(2), O.toResult('array is empty'))
  }),
  R.map(A.join('-')),
  R.toNullable,
) // → 'ts-belt'
pipe(
  R.fromNullable([], 'cannot be nullable'),
  R.flatMap(xs => {
    return pipe(xs, A.dropExactly(2), O.toResult('array is empty'))
  }),
  R.map(A.join('-')),
  R.toNullable,
) // → null
toOption
Returns Some(value) if result is Ok(value), otherwise, returns None, both Some and None come from the Option type.
function toOption<A, B>(result: Result<A, B>): Option<A>
// type Data = { names: string } | null
pipe(
  // ⬇️ const obj: Data = { names: 'hello,world,ts,belt' }
  R.fromNullable(obj, 'cannot be nullable'), // → Ok({ names: 'hello,world,ts,belt' })
  R.map(D.getUnsafe('names')), // → Ok('hello,world,ts,belt')
  R.map(S.split(',')), // → Ok(['hello', 'world', 'ts', 'belt'])
  R.toOption, // → Some(['hello', 'world', 'ts', 'belt'])
  O.flatMap(A.dropExactly(2)), // → Some(['ts', 'belt'])
  O.map(A.join('-')), // → Some('ts-belt')
  O.getWithDefault('nothing found'), // → ts-belt
) // → 'ts-belt'
pipe(
  // ⬇️ const obj: Data = null
  R.fromNullable(obj, 'cannot be nullable'), // → Error('cannot be nullable')
  R.map(D.getUnsafe('names')), // → Error('cannot be nullable')
  R.map(S.split(',')), // → Error('cannot be nullable')
  R.toOption, // → None
  O.flatMap(A.dropExactly(2)), // → None
  O.map(A.join('-')), // → None
  O.getWithDefault('nothing found'), // → nothing found
) // → 'nothing found'
toUndefined
Returns value if result is Ok(value), otherwise, returns undefined.
function toUndefined<A, B>(result: Result<A, B>): A | undefined
pipe(
  R.fromNullable(['hello', 'world', 'ts', 'belt'], 'cannot be nullable'),
  R.flatMap(xs => {
    return pipe(xs, A.dropExactly(2), O.toResult('array is empty'))
  }),
  R.map(A.join('-')),
  R.toUndefined,
) // → 'ts-belt'
pipe(
  R.fromNullable([], 'cannot be nullable'),
  R.flatMap(xs => {
    return pipe(xs, A.dropExactly(2), O.toResult('array is empty'))
  }),
  R.map(A.join('-')),
  R.toUndefined,
) // → undefined