Result
Result
represents the result of an operation that can either be successful (Ok
) or a failure (Error
). It's commonly used to represent the result of a function that may fail, such as a network request, a file read, or a database query.
Constructors
ok
Constructs an Ok
Result
with the provided value.
import { Result } from 'funkcia';
// ββββ Result<number, never>
// βΌ
const result = Result.ok(10);
of
Constructs an Ok
Result
with the provided value.
import { Result } from 'funkcia';
// ββββ Result<number, never>
// βΌ
const result = Result.of(10);
error
Constructs an Error
result with the provided value.
import { Result } from 'funkcia';
function divide(dividend: number, divisor: number): Result<number, InvalidDivisor> {
if (divisor === 0) {
return Result.error(new InvalidDivisor());
}
return Result.ok(dividend / divisor);
}
fromNullable
Constructs a Result
from a nullable value.
If the value is null
or undefined
, it returns a Result.Error
with a NoValueError
error, or with the value returned by the provided onNullable
callback. Otherwise, it returns a Result.Ok
.
import { Result } from 'funkcia';
declare const user: User | null;
// ββββ Result<User, NoValueError>
// βΌ
const result = Result.fromNullable(user);
// ββββ Result<string, UserNotFound>
// βΌ
const resultWithCustomError = Result.fromNullable(
user,
() => new UserNotFound(),
);
fromFalsy
Constructs a Result
from a falsy value.
If the value is falsy, it returns a Result.Error
result with a NoValueError
error, or with the value returned by the provided onFalsy
callback. Otherwise, it returns a Result.Ok
.
import { Result } from 'funkcia';
interface User {
id: string;
firstName: string;
lastName: string | null;
}
// ββββ Result<string, NoValueError>
// βΌ
const result = Result.fromFalsy(user.lastName?.trim());
// ββββ Result<string, Error>
// βΌ
const resultWithCustomError = Result.fromFalsy(
user.lastName?.trim(),
() => new Error('User missing last name'),
);
try
Constructs a Result
from a function that may throw.
If the function executes successfully, it returns a Result.Ok
. Otherwise, it returns a Result.Error
containing an UnknownError
with the thrown exception, or with the value returned by the provided onThrow
callback.
import { Result } from 'funkcia';
// ββββ Result<URL, UnknownError>
// βΌ
const url = Result.try(() => new URL('example.com'));
// Output: Error(UnknownError)
// ββββ Result<URL, Error>
// βΌ
const urlWithCustomError = Result.try(
() => new URL('example.com'),
(error) => new Error('Invalid URL'),
);
// Output: Error('Invalid URL')
predicate
Returns a function that asserts that a value passes the test implemented by the provided function, creating a Result.Ok
, with the value tested, if the predicate is fulfilled.
If the test fails, returns a Result.Error
with a FailedPredicateError
instead, or with the value returned by the provided onUnfulfilled
callback.
import { Result } from 'funkcia';
// With type guard
// ββββ (shape: Shape) => Result
// βΌ
const ensureCircle = Result.predicate(
(shape: Shape): shape is Circle => shape.kind === 'circle',
() => new InvalidShapeError(shape.kind),
);
// With regular predicate
// ββββ (value: number) => Result
// βΌ
const ensurePositive = Result.predicate(
(value: number) => value > 0,
(value) => new InvalidNumberError(value),
);
fun
This method improves the inference of the function's return value and guarantees that it will always return a Result
. It is extremely useful when your function can return multiple errors.
Declare a function that always returns a Result
.
import { Result } from 'funkcia';
// When defining a normal function allowing typescript to infer the return type,
// the return type is always a union of `Result<T, never>` and `Result<never, E>`
function hasAcceptedTermsOfService(user: User) {
if (typeof user.termsOfService !== 'boolean') {
return Result.error('NOT ACCEPTED' as const);
}
return user.termsOfService ?
Result.ok('ACCEPTED' as const)
: Result.error('REJECTED' as cons);
}
// ββββ Result<'ACCEPTED', never> | Result<never, 'REJECTED'> | Result<never, 'NOT ACCEPTED'>
// βΌ
const result = hasAcceptedTermsOfService(user);
// When using the `fun` method, the return type is always `Result<T, E>`
const improvedHasAcceptedTermsOfService = Result.fun(hasAcceptedTermsOfService);
// ββββ Result<'ACCEPTED', 'REJECTED' | 'NOT ACCEPTED'>
// βΌ
const result = improvedHasAcceptedTermsOfService(user);
enhance
Converts a function that may throw an exception to a function that returns a Result
.
import { Result } from 'funkcia';
// ββββ (text: string, reviver?: Function) => Result<any, TypeError>
// βΌ
const safeJsonParse = Result.enhance(
JSON.parse,
(error) => new TypeError('Invalid JSON'),
);
// ββββ Result<any, TypeError>
// βΌ
const result = safeJsonParse('{ "name": "John Doe" }');
// Output: Ok({ name: 'John Doe' })
values
Given an array of Result
s, returns an array containing only the values inside Result.Ok
.
import { Result } from 'funkcia';
// ββββ number[]
// βΌ
const output = Result.values([
Result.ok(1),
Result.error<number>('Failed computation'),
Result.ok(3),
]);
// Output: [1, 3]
is
Asserts that an unknown value is a Result
.
import { Result } from 'funkcia';
declare const maybeAResultWithUser: unknown;
if (Result.is(maybeAResultWithUser)) {
// ββββ Result<unknown, unknown>
// βΌ
const user = maybeAResultWithUser.filter(isUser);
// β²
// ββββ Result<User, FailedPredicateError<unknown>>
}
Instance methods
map
Applies a callback function to the value of the Result
when it is Ok
, returning a new Result
containing the new value.
import { Result } from 'funkcia';
// ββββ Result<number, never>
// βΌ
const result = Result.ok(10).map(number => number * 2);
// Output: Ok(20)
mapError
Applies a callback function to the value of the Result
when it is Error
, returning a new Result
containing the new error value.
import { Result } from 'funkcia';
declare const user: User | null;
// ββββ Result<User, UserNotFound>
// βΌ
const result = Result.fromNullable(user).mapError(
(error) => new UserNotFound()
// β²
// ββββ NoValueError
);
mapBoth
Maps both the Result
value and the Result
error to new values.
import { Result } from 'funkcia';
// ββββ Result<string, UserMissingInformationError>
// βΌ
const result = Result.fromNullable(user.lastName).mapBoth({
Ok: (lastName) => `Hello, Mr. ${lastName}`,
Error: (error) => new UserMissingInformationError(),
// β²
// ββββ NoValueError
});
andThen
Applies a callback function to the value of the Result
when it is Ok
, and returns the new value. Similar to chain
(also known as flatMap
).
import { Result } from 'funkcia';
declare function readFile(path: string): Result<string, FileNotFoundError | FileReadError>;
declare function parseJsonFile(contents: string): Result<FileContent, InvalidJsonError>;
// ββββ Result<FileContent, FileNotFoundError | FileReadError | InvalidJsonError>
// βΌ
const result = readFile('data.json').andThen(parseJsonFile);
filter
Asserts that the Result
value passes the test implemented by the provided function. Can narrow types and customize error handling.
import { Result } from 'funkcia';
declare const input: Shape;
// ββββ Result<Circle, FailedPredicateError<Square>>
// βΌ
const result = Result.of(input).filter(
(shape): shape is Circle => shape.kind === 'CIRCLE',
);
// ββββ Result<Circle, Error>
// βΌ
const resultWithCustomError = Result.of(input).filter(
(shape): shape is Circle => shape.kind === 'CIRCLE',
(shape) => new Error(`Expected Circle, received ${shape.kind}`),
// β²
// ββββ Square
);
or
Replaces the current Result
with the provided fallback Result
when it is Error
.
import { Result } from 'funkcia';
const personalEmail = Result.ok('[email protected]')
.or(() => Result.ok('[email protected]'))
.unwrap();
// Output: '[email protected]'
const workEmail = Result.error(new Error('Missing personal email'))
.or(() => Result.ok('[email protected]'))
.unwrap();
// Output: '[email protected]'
swap
Swaps the Result
value and error. If Ok
, returns Error
with the value. If Error
, returns Ok
with the error.
import { Result } from 'funkcia';
declare function getCachedUser(email: Email): Result<User, CacheMissError<Email>>;
declare function findOrCreateUserByEmail(email: Email): User;
// ββββ Result<User, User>
// βΌ
const result = getCachedUser('[email protected]')
.swap() // Result<CacheMissError<Email>, User>
.map((cacheMiss) => findOrCreateUserByEmail(cacheMiss.input));
// β²
// ββββ CacheMissError<Email>
zip
Combines two Result
s into a single Result
containing a tuple of their values, if both Result
s are Ok
variants, otherwise, returns Result.Error
.
import { Result } from 'funkcia';
const first = Result.some('hello');
const second = Result.some('world');
// ββββ Result<[string, string], never>
// βΌ
const strings = first.zip(second);
// Output: Ok(['hello', 'world'])
zipWith
Combines two Result
s into a single Result
. The new value is produced by applying the given function to both values, if both Result
s are Ok
variants, otherwise, returns Error
.
import { Result } from 'funkcia';
const first = Result.some('hello');
const second = Result.some('world');
// ββββ Result<string, never>
// βΌ
const greeting = first.zipWith(second, (a, b) => `${a} ${b}`);
// Output: Ok('hello world')
match
Compare the Result
against the possible patterns and then execute code based on which pattern matches.
import { Result } from 'funkcia';
declare function readFile(path: string): Result<string, FileNotFoundError | FileReadError>;
declare function parseSalesRecords(content: string): Result<SalesRecord[], InvalidSalesRecordFileError>;
declare function aggregateSales(salesRecords: SalesRecord[]): AggregatedSaleRecord[];
// ββββ AggregatedSaleRecord[]
// βΌ
const data = readFile('data.json')
.andThen(parseSalesRecords)
.match({
Ok(contents) {
return aggregateSales(contents);
},
// ββββ FileNotFoundError | FileReadError | InvalidSalesRecordFileError
// βΌ
Error(error) {
return []
},
});
unwrap
Throws UnwrapError
if the Result
is Error
.
Unwraps the Result
value.
import { Result } from 'funkcia';
// ββββ number
// βΌ
const number = Result.ok(10).unwrap();
Result.error(new Error('Β―\_(γ)_/Β―')).unwrap();
// Output: Uncaught exception: 'called "Result.unwrap()" on an "Error" value'
unwrapError
Throws UnwrapError
if the Result
is Ok
.
Unwraps the Result
error.
import { Result } from 'funkcia';
const result = Result.error(new UserNotFound());
if (result.isError()) {
const error = result.unwrapError();
// β²
// ββββ UserNotFound
}
unwrapOr
Returns the value of the Result
. If the Result
is Error
, returns the fallback value.
import { Result } from 'funkcia';
// ββββ string
// βΌ
const baseUrl = Result.ok('https://funkcia.lukemorales.io')
.unwrapOr(() => 'http://localhost:3000');
// Output: 'https://funkcia.lukemorales.io'
const apiKey = Result.error('Missing API key')
.unwrapOr(() => 'sk_test_9FK7CiUnKaU');
// Output: 'sk_test_9FK7CiUnKaU'
unwrapOrNull
Unwraps the value of the Result
if it is a Ok
, otherwise returns null
.
import { Result } from 'funkcia';
declare const findUserById: (id: string) => Result<User, UserNotFound>;
// ββββ User | null
// βΌ
const user = findUserById('user_123').unwrapOrNull();
unwrapOrUndefined
Unwraps the value of the Result
if it is a Ok
, otherwise returns undefined
.
import { Result } from 'funkcia';
declare const findUserById: (id: string) => Result<User, UserNotFound>;
// ββββ User | undefined
// βΌ
const user = findUserById('user_123').unwrapOrUndefined();
expect
Throws the provided Error if the Result
is Error
.
Unwraps the Result
value.
import { Result } from 'funkcia';
declare function findUserById(id: string): Result<User, NoValueError>;
// ββββ User
// βΌ
const user = findUserById(userId).expect(
(error) => new UserNotFound(userId)
// β²
// ββββ NoValueError
);
merge
Returns the value inside the Result
. If Ok
, returns the value; if Error
, returns the error.
import { Result } from 'funkcia';
declare function getCachedUser(email: Email): Result<User, CacheMissError<Email>>;
declare function getOrCreateUserByEmail(email: Email): User;
// ββββ User
// βΌ
const result = getCachedUser('[email protected]')
.swap() // Result<CacheMissError<Email>, User>
.map((cacheMiss) => getOrCreateUserByEmail(cacheMiss.input)) // Result<User, User>
.merge();
// Output: { id: 'user_123', email: '[email protected]' }
contains
Returns true
if the predicate is fulfilled by the wrapped value. If the predicate is not fulfilled or the Result
is Error
, it returns false
.
import { Result } from 'funkcia';
// ββββ boolean
// βΌ
const isPositive = Result.ok(10).contains(num => num > 0);
// Output: true
const isNegative = Result.error(10).contains(num => num < 0);
// Output: false
toArray
Converts a Result
to an array. If Result
is Ok
, returns an array with the value. If Result
is Error
, returns an empty array.
import { Result } from 'funkcia';
// ββββ number[]
// βΌ
const output = Result.ok(10).toArray();
// Output: [10]
toOption
Converts a Result
to an Option
. If Result
is Ok
, returns an Option.Some
. If Result
is Error
, returns an Option.None
.
import { Result } from 'funkcia';
declare function readFile(path: string): Result<string, FileNotFoundError | FileReadError>;
declare function parseSalesRecords(content: string): Result<SalesRecord[], InvalidSalesRecordFileError>;
// ββββ Option<SalesRecord[]>
// βΌ
const fileContents = readFile('data.json')
.andThen(parseSalesRecords)
.toOption();
toAsyncOption
Converts the Result
to an AsyncOption
.
import { Result } from 'funkcia';
declare function readFile(path: string): Result<string, FileNotFoundError | FileReadError>;
declare function parseSalesRecords(content: string): Result<SalesRecord[], InvalidSalesRecordFileError>;
// ββββ AsyncOption<SalesRecord[]>
// βΌ
const asyncFile = readFile('data.json')
.andThen(parseSalesRecords)
.toAsyncOption();
// Output: Promise<Some(SalesRecord[])>
toAsyncResult
Converts the Result
to an AsyncResult
.
import { Result } from 'funkcia';
declare function readFile(path: string): Result<string, FileNotFoundError | FileReadError>;
declare function parseSalesRecords(content: string): Result<SalesRecord[], InvalidSalesRecordFileError>;
// ββββ AsyncResult<SalesRecord[], FileNotFoundError | FileReadError | InvalidSalesRecordFileError>
// βΌ
const asyncFile = readFile('data.json')
.andThen(parseSalesRecords)
.toAsyncResult();
// Output: Promise<Ok(SalesRecord[])>
tap
Calls the function with the Result
value, then returns the Result
itself, ignoring the returned value of the provided function.
This allows "tapping into" a function sequence in a pipe, to perform side effects on intermediate results.
import { Result } from 'funkcia';
// ββββ Result<number, never>
// βΌ
const result = Result.some(10).tap(
(value) => console.log(value), // Console output: 10
);
tapError
Calls the function with the Result
error, then returns the Result
itself, ignoring the returned value of the provided function.
This allows "tapping into" a function sequence in a pipe, to perform side effects on intermediate results.
import { Result } from 'funkcia';
declare function findUserById(id: string): Result<User, UserNotFound>;
// ββββ Result<User, UserNotFound>
// βΌ
const result = findUserById('invalid_id').tapError(
(error) => console.log(error), // Console output: UserNotFound
);
isOk
Returns true
if the Result
contains a value.
import { Option } from 'funkcia';
declare function findUserById(id: string): Result<User, UserNotFound>;
const user = findUserById('user_123');
if (user.isOk()) {
return user.unwrap(); // `unwrap` will not throw
}
isError
Returns true
if the Result
contains an error.
import { Option } from 'funkcia';
declare function findUserById(id: string): Result<User, UserNotFound>;
const user = findUserById('invalid_id');
if (user.isError()) {
const error = user.unwrapError(); // `unwrapError` will not throw
// ...process error
}
return user.unwrap();
equals
Compares the Result
with another Result
and returns true
if they are equal.
import { Result } from 'funkcia';
const result = Result.of(10).equals(Result.ok(10));
// Output: true
Last updated
Was this helpful?