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