Skip to main content

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>
Live Editor
Result
Loading...

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