ResultAsync
AsyncResult represents a Promise that never rejects of an operation that can either succeed (Ok) or return an error (Error). Every AsyncResult resolves to a Result.Ok when successful or Result.Error when it fails.
An AsyncResult allows you to chain the same methods as a Result, but in an asynchronous context. This empowers you to write code and manipulate data in a seamless, synchronous-like manner without worrying about awaiting Promises.
By awaiting the AsyncResult, the Promise inside will resolve to the underlying Result.
Constructors
ok
Constructs an AsyncResult that resolves to a Result.Ok with the provided value.
import { AsyncResult } from 'funkcia';
// ββββ AsyncResult<number, never>
// βΌ
const result = AsyncResult.ok(10);
// Promise<Ok(10)>of
Constructs an AsyncResult that resolves to a Result.Ok with the provided value.
import { AsyncResult } from 'funkcia';
// ββββ AsyncResult<number, never>
// βΌ
const result = AsyncResult.of(10);
// Promise<Ok(10)>error
Constructs an AsyncResult that resolves to a Result.Error with the provided value.
import { AsyncResult } from 'funkcia';
async function rateLimit(clientId: ClientId, ip: IpAddress): AsyncResult<ClientId, RateLimitError> {
const attempts = await cache.get(`ratelimit:${clientId}:${ip}`)
if (attempts.total > 10) {
return AsyncResult.error(new RateLimitError({ clientId, ip }));
}
return AsyncOption.ok(clientId);
}fromNullable
Constructs an AsyncResult from a nullable value.
If the value is null or undefined, it resolves to a Result.Error with a NoValueError exception or a custom error. Otherwise, it resolves to a Result.Ok.
import { AsyncResult } from 'funkcia';
// With default error handling
// ββββ AsyncResult<string, NoValueError>
// βΌ
const result = AsyncResult.fromNullable(localStorage.getItem('@app/theme'));
// With custom error handling
// ββββ AsyncResult<string, Error>
// βΌ
const result = AsyncResult.fromNullable(
localStorage.getItem('@app/theme'),
() => new Error('Theme not set'),
);fromFalsy
Constructs an AsyncResult from a falsy value.
If the value is falsy, it resolves to a Result.Error with a NoValueError exception or a custom error. Otherwise, it resolves to a Result.Ok.
import { AsyncResult } from 'funkcia';
interface User {
id: string;
firstName: string;
lastName: string | null;
}
// With default error handling
// ββββ AsyncResult<string, NoValueError>
// βΌ
const result = AsyncResult.fromFalsy(user.lastName?.trim());
// With custom error handling
// ββββ AsyncResult<string, Error>
// βΌ
const result = AsyncResult.fromFalsy(
user.lastName?.trim(),
() => new Error('User missing last name'),
);try
Constructs an AsyncResult from a promise that may reject or return a Result.
Provides multiple overloads for different promise return types and error handling strategies.
import { AsyncResult } from 'funkcia';
// With Result-returning promise
declare async function findUserById(id: string): Promise<Result<User, UserNotFound>>;
// ββββ AsyncResult<User, UserNotFound | UnknownError>
// βΌ
const url = AsyncResult.try(() => findUserById('user_123'));
// Output: Promise<Ok(User)>
// With value-returning promise
declare async function findUserByIdOrThrow(id: string): Promise<User>;
// ββββ AsyncResult<User, UnknownError>
// βΌ
const url = AsyncResult.try(() => findUserByIdOrThrow('user_123'));
// Output: Promise<Error(UnknownError)>
// With custom error handling
// ββββ AsyncResult<User, UserNotFound | DatabaseFailureError>
// βΌ
const url = AsyncResult.try(
() => findUserByIdOrThrow('user_123'),
(error) => UserNotFound.is(error) ? error : new DatabaseFailureError(error),
);
// Output: Promise<Error(DatabaseFailureError('Error: Failed to connect to the database'))>promise
Constructs an AsyncResult from a Promise that returns a Result, and never rejects.
import { AsyncResult } from 'funkcia';
declare async function findUserById(id: string): Promise<Result<User, UserNotFound | DatabaseFailureError>>;
// ββββ AsyncResult<User, UserNotFound | DatabaseFailureError>
// βΌ
const result = AsyncResult.promise(() => findUserById('user_123'));
// Output: Promise<Ok(User)>liftPromise
Lifts a Promise that may fail or resolve to a Result to a function that returns an AsyncResult. Provides multiple overloads for different promise return types and error handling strategies.
import { AsyncResult } from 'funkcia';
// With Result-returning promise
declare async function findUserById(id: string): Promise<Result<User, UserNotFound>>;
// ββββ (id: string) => AsyncResult<User, UserNotFound | UnknownError>
// βΌ
const safeFindUserById = AsyncResult.liftPromise(findUserById);
// With value-returning promise
declare async function findUserByIdOrThrow(id: string): Promise<User>;
// ββββ (id: string) => AsyncResult<User, UnknownError>
// βΌ
const safeFindUserById = AsyncResult.liftPromise(findUserByIdOrThrow);
// With custom error handling
// ββββ (id: string) => AsyncResult<User, UserNotFound | DatabaseFailureError>
// βΌ
const safeFindUserById = AsyncResult.liftPromise(
findUserByIdOrThrow,
(error) => UserNotFound.is(error) ? error : new DatabaseFailureError(error),
);predicate
Returns a function that asserts that a value passes the test implemented by the provided function. Provides multiple overloads for type guards and regular predicates, with optional custom error handling.
import { AsyncResult } from 'funkcia';
// With type guard
declare const input: Shape;
// ββββ (shape: Shape) => AsyncResult<Circle, FailedPredicateError<Square>>
// βΌ
const ensureCircle = AsyncResult.predicate(
(shape: Shape): shape is Circle => shape.kind === 'circle',
);
// ββββ AsyncResult<Circle, FailedPredicateError<Square>>
// βΌ
const result = ensureCircle(input);
// With regular predicate
// ββββ (value: number) => AsyncResult<number, FailedPredicateError<number>>
// βΌ
const ensurePositive = AsyncResult.predicate(
(value: number) => value > 0,
);
// ββββ AsyncResult<number, FailedPredicateError<number>>
// βΌ
const result = ensurePositive(10);
// Output: Ok(10)
// With custom error handling
// ββββ (shape: Shape) => AsyncResult<Circle, InvalidShapeError>
// βΌ
const ensureCircle = AsyncResult.predicate(
(shape: Shape): shape is Circle => shape.kind === 'circle',
// ββββ Square
// βΌ
(shape) => new InvalidShapeError(shape.kind),
);Combinators
values
Given an array of AsyncResults, returns an array containing only the values inside Result.Ok.
import { AsyncResult } from 'funkcia';
// ββββ number[]
// βΌ
const output = await AsyncResult.values([
AsyncResult.ok(1),
AsyncResult.error<number>('Failed computation'),
AsyncResult.ok(3),
]);
// Output: [1, 3]zip
Combines two AsyncResults into a single AsyncResult containing a tuple of their values, if both AsyncResults resolve to Result.Ok variants, otherwise, resolves to Result.Error.
import { AsyncResult } from 'funkcia';
const first = AsyncResult.some('hello');
const second = AsyncResult.some('world');
// ββββ AsyncResult<[string, string], never>
// βΌ
const strings = first.zip(second);
// Output: Promise<Ok(['hello', 'world'])>zipWith
Combines two AsyncResults into a single AsyncResult. The new value is produced by applying the given function to both values, if both AsyncResults resolve to Result.Ok variants, otherwise, resolves to Result.Error.
import { AsyncResult } from 'funkcia';
const first = AsyncResult.some('hello');
const second = AsyncResult.some('world');
// ββββ AsyncResult<string, never>
// βΌ
const greeting = first.zipWith(second, (a, b) => `${a} ${b}`);
// Output: Promise<Ok('hello world')>Conversions
then
Attaches a callback for the resolution of the Promise inside the AsyncResult.
match
Returns a promise that compares the underlying Result against the possible patterns, and then execute code based on which pattern matches.
import { AsyncResult } from 'funkcia';
import { readFileSync } from 'node:fs';
declare function readFile(path: string): AsyncResult<string, FileNotFoundError | FileReadError>;
declare function parseJsonFile(contents: string): AsyncResult<FileContent, InvalidJsonError>;
// ββββ string
// βΌ
const data = await readFile('data.json')
.andThen(parseJsonFile)
.match({
Ok(contents) {
return 'File is valid JSON';
},
// ββββ FileNotFoundError | FileReadError | InvalidJsonError
// βΌ
Error(error) {
return 'File is invalid JSON';
},
});unwrap
Returns a promise that unwraps the underlying AsyncResult value. Throws UnwrapError if the Result is Error.
import { AsyncResult } from 'funkcia';
// ββββ User
// βΌ
const user = await AsyncResult.ok(databaseUser).unwrap();
const team = await AsyncResult.error(new TeamNotFound()).unwrap();
// Output: Uncaught exception: 'called "Result.unwrap()" on an "Error" value'unwrapError
Returns a promise that unwraps the underlying Result error. Throws UnwrapError if the Result is Ok.
import { AsyncResult } from 'funkcia';
// ββββ UserNotFound
// βΌ
const error = await AsyncResult.error(new UserNotFound()).unwrapError();unwrapOr
Returns a promise that unwraps the underlying Result. If the promise resolves to a Result.Error, returns the result of the provided callback.
import { AsyncResult } from 'funkcia';
// ββββ string
// βΌ
const baseUrl = await AsyncResult.ok(process.env.BASE_URL)
.unwrapOr(() => 'http://localhost:3000');
// Output: 'https://funkcia.lukemorales.io'
const apiKey = await AsyncResult.error('Missing API key')
.unwrapOr(() => 'sk_test_9FK7CiUnKaU');
// Output: 'sk_test_9FK7CiUnKaU'unwrapOrNull
Returns a promise that unwraps the value of the underlying Result if it is a Result.Ok, otherwise returns null.
import { AsyncResult } from 'funkcia';
// ββββ User | null
// βΌ
const user = await AsyncResult.ok(databaseUser).unwrapOrNull();unwrapOrUndefined
Returns a promise that unwraps the value of the underlying Result if it is a Result.Ok, otherwise returns undefined.
import { AsyncResult } from 'funkcia';
// ββββ User | undefined
// βΌ
const user = await AsyncResult.ok(databaseUser).unwrapOrUndefined();expect
Returns a promise that unwraps the underlying Result value. Throws the provided Error if the Result is Error.
import { AsyncResult } from 'funkcia';
declare function findUserById(id: string): AsyncResult<User, DatabaseFailureError>;
// ββββ User
// βΌ
const user = await findUserById('user_123').expect(
(error) => new UserNotFound(userId),
// β²
// ββββ DatabaseFailureError
);
const anotherUser = await findUserById('invalid_id').expect(
(error) => new UserNotFound('team_123'),
// β²
// ββββ DatabaseFailureError
);
// Output: Uncaught exception: 'User not found: "user_123"'merge
Returns a promise that unwraps the underlying Result. If the Result is Ok, resolves to the value inside the Ok variant. If the Result is Error, resolves to the value inside the Error variant.
import { AsyncResult } from 'funkcia';
declare function getCachedUser(email: Email): AsyncResult<User, CacheMissError<Email>>;
declare function findOrCreateUserByEmail(email: Email): AsyncResult<User, never>;
// ββββ User
// βΌ
const result = await getCachedUser('[email protected]')
.swap() // AsyncResult<CacheMissError<Email>, User>
.andThen((cacheMiss) => findOrCreateUserByEmail(cacheMiss.input)) // AsyncResult<User, User>
.merge();
// Output: { id: 'user_123', email: '[email protected]' }contains
Returns a Promise that verifies if the Result contains a value that passes the test implemented by the provided function.
import { AsyncResult } from 'funkcia';
// ββββ boolean
// βΌ
const isPositive = await AsyncResult.some(10).contains(num => num > 0);
// Output: truetoAsyncOption
Converts the AsyncResult to an AsyncOption. If the resolved Result is Ok, returns an AsyncOption.Some. If the resolved Result is Error, returns an AsyncOption.None.
import { AsyncResult } from 'funkcia';
declare function readFile(path: string): AsyncResult<string, FileNotFoundError | FileReadError>;
declare function parseJsonFile(contents: string): AsyncResult<FileContent, InvalidJsonError>;
// ββββ AsyncOption<FileContent>
// βΌ
const asyncFile = readFile('data.json')
.andThen(parseJsonFile)
.toAsyncOption();
// Output: Promise<Some(FileContent)>toArray
Returns a Promise that converts the underlying Result to an array. If the resolved Result is Ok, returns an array with the value. If the resolved Result is Error, returns an empty array.
import { AsyncResult } from 'funkcia';
// ββββ number[]
// βΌ
const output = await AsyncResult.some(10).toArray();
// Output: [10]Transformations
map
Applies a callback function to the value of the AsyncResult when it is Ok, returning a new AsyncResult containing the new value.
import { AsyncResult } from 'funkcia';
// ββββ AsyncResult<number, never>
// βΌ
const result = AsyncResult.ok(10).map(number => number * 2);
// Output: Promise<Ok(20)>mapError
Applies a callback function to the value of the AsyncResult when it is Error, returning a new AsyncResult containing the new error value.
import { AsyncResult } from 'funkcia';
// ββββ AsyncResult<string, UserMissingInformationError>
// βΌ
const result = AsyncResult.fromNullable(user.lastName).mapError(
(error) => new UserMissingInformationError()
// β²
// ββββ NoValueError
);andThen
Applies a callback function to the value of the AsyncResult when it is Ok, and returns the new value. Supports both Result and AsyncResult returns.
import { AsyncResult } from 'funkcia';
// With Result return
declare function readFile(path: string): AsyncResult<string, FileNotFoundError | FileReadError>;
declare function parseJsonFile(contents: string): Result<FileContent, InvalidJsonError>;
// ββββ AsyncResult<FileContent, FileNotFoundError | FileReadError | InvalidJsonError>
// βΌ
const result = readFile('data.json')
.andThen(parseJsonFile);
// With AsyncResult return
declare function parseJsonFileAsync(contents: string): AsyncResult<FileContent, InvalidJsonError>;
// ββββ AsyncResult<FileContent, FileNotFoundError | FileReadError | InvalidJsonError>
// βΌ
const result = readFile('data.json')
.andThen(parseJsonFileAsync);filter
Asserts that the AsyncResult value passes the test implemented by the provided function. Supports type guards and regular predicates with optional custom error handling.
import { AsyncResult } from 'funkcia';
// With type guard
declare const input: Shape;
// ββββ AsyncResult<Circle, FailedPredicateError<Square>>
// βΌ
const result = AsyncResult.of(input).filter(
(shape): shape is Circle => shape.kind === 'CIRCLE',
);
// With regular predicate
// ββββ AsyncResult<string, FailedPredicateError<string>>
// βΌ
const result = AsyncResult.of(user.lastName).filter(
(value) => value.length > 0,
);
// With custom error handling
// ββββ AsyncResult<string, Error>
// βΌ
const result = AsyncResult.of(user.lastName).filter(
(value) => value.length > 0,
(value) => new Error(`Expected non-empty string, received ${value}`),
);Fallbacks
or
Replaces the current AsyncResult with the provided fallback AsyncResult when it is Error. If the resolved Result is Ok, it returns the current AsyncResult.
import { AsyncResult } from 'funkcia';
// ββββ string
// βΌ
const result = await AsyncResult.ok('Smith')
.or(() => AsyncResult.ok('John'))
.unwrap();
// Output: 'Smith'
const greeting = await AsyncResult.error(new Error('Missing user'))
.or(() => AsyncResult.ok('John'))
.unwrap();
// Output: 'John'swap
Swaps the AsyncResult value and error.
If the underlying Result is Ok, it returns a AsyncResult that resolves to a Result.Error with the value. If the underlying Result is Error, it returns a AsyncResult that resolves to a Result.Ok with the error.
import { AsyncResult } from 'funkcia';
declare function getCachedUser(email: Email): AsyncResult<User, CacheMissError<Email>>;
declare function findOrCreateUserByEmail(email: Email): AsyncResult<User, never>;
// ββββ Result<User, User>
// βΌ
const result = getCachedUser('[email protected]')
.swap()
.andThen((cacheMiss) => findOrCreateUserByEmail(cacheMiss.input));
// β²
// ββββ CacheMissError<Email>Other Utilities
tap
Calls the function with Result value, then returns the Result itself. The return value of the provided function is ignored.
import { AsyncResult } from 'funkcia';
declare function authenticateUser(credentials: AuthCredentials): AsyncResult<User, UserNotFound | InvalidCredentials>;
declare async function registerSuccessfulLoginAttempt(user: User): Promise<{ loginAttempts: number }>;
// ββββ AsyncResult<User, UserNotFound | InvalidCredentials>
// βΌ
const result = authenticateUser(req.body).tap(async (user) => {
return await registerSuccessfulLoginAttempt(user);
});
// Output: Promise<Ok(User)>tapError
Calls the function with the underlying Result error, then returns the AsyncResult itself. The return value of the provided function is ignored.
import { AsyncResult } from 'funkcia';
declare function authenticateUser(credentials: AuthCredentials): AsyncResult<User, UserNotFound | InvalidCredentials>;
declare async function registerLoginAttempt(user: User): Promise<{ loginAttempts: number }>;
// ββββ AsyncResult<User, UserNotFound | InvalidCredentials>
// βΌ
const result = authenticateUser(req.body).tapError(async (error) => {
if (InvalidCredentials.is(error)) {
return await registerLoginAttempt(error.email);
}
});
// Output: Promise<Error(InvalidCredentials)>Last updated
Was this helpful?