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 OkResult with the provided value.
import { Result } from 'funkcia';
// ┌─── Result<number, never>
// â–¼
const result = Result.ok(10);
of
Alias of Result.ok
Constructs an OkResult 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.
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('johndoe@example.com')
.swap() // Result<CacheMissError<Email>, User>
.map((cacheMiss) => findOrCreateUserByEmail(cacheMiss.input));
// â–²
// └─── CacheMissError<Email>
zip
Combines two Results into a single Result containing a tuple of their values, if both Results 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 Results into a single Result. The new value is produced by applying the given function to both values, if both Results 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.
Use this method at the edges of the system, when storing values in a database or serializing to JSON.
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
Use this method at the edges of the system, when storing values in a database or serializing to JSON.
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('johndoe@example.com')
.swap() // Result<CacheMissError<Email>, User>
.map((cacheMiss) => getOrCreateUserByEmail(cacheMiss.input)) // Result<User, User>
.merge();
// Output: { id: 'user_123', email: 'johndoe@example.com' }
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