This library provides two utility classes to manage success/failure outcomes in synchronous and asynchronous operations in a clean, predictable, and type-safe manner:
Result<T, E>for synchronous operationsAsyncResult<T, E>for asynchronous operations (Promise-wrapped)
- Explicit success and error handling
- Pattern-matching-like API (
isOk(),isFail()) - Safe transformations with
map() - Optional throwing or fallback value with
orElseThrow(),orElse() - Asynchronous equivalents via
AsyncResult
// userService.ts
import db from './db';
import { User } from './types';
export async function getUserById(id: string): Promise<User> {
const user = await db.user.findUnique({ where: { id } });
if (!user) throw new Error("User not found");
return user;
}
// userController.ts
import { getUserById } from './userService';
import { AsyncResult } from './result';
async function handleGetUserRequest(req, res) {
const getUserByIdResult = new AsyncResult(getUserById(req.params.id));
const isOk = await getUserByIdResult.isOkAsync();
if (isOk) {
res.status(200).json(userResult.value);
return;
}
res.status(404).json({ error: userResult.value.message });
}// validate.ts
import { Result } from './result';
import { z, ZodSchema } from 'zod';
export function validateSchema<T>(schema: ZodSchema<T>, data: unknown): Result<T, Error> {
try {
const parsed = schema.parse(data);
return Result.success(parsed);
} catch (err) {
return Result.error(err instanceof Error ? err : new Error("Validation failed"));
}
}
// usage
const userSchema = z.object({
name: z.string().min(1),
age: z.number().int().positive()
});
const input = { name: "", age: -2 };
const validation = validateSchema(userSchema, input);
if (validation.isFail()) {
console.error("Validation failed:", validation.value.message);
} else {
console.log("Valid data:", validation.value);
}In an async utility function that processes a result pipeline with AsyncResult, performing validation, mapping, and fallbacks:
// processPayment.ts
import { AsyncResult, Result } from './result';
async function chargeCard(amount: number): Promise<string> {
if (amount <= 0) throw new Error("Invalid amount");
// Simulate async payment
return `Payment of $${amount} processed`;
}
export async function processPayment(amount: number): Promise<Result<string, Error>> {
const result = new AsyncResult(chargeCard(amount))
.validate((res) => res.includes("processed"), new Error("Payment failed"))
.map((res) => res.toUpperCase());
const finalMessage = await result.orElseAsync("DEFAULT PAYMENT MESSAGE");
// Return a resolved Result
return Result.success(finalMessage);
}
// usage
const result = await processPayment(100);
if (result.isOk()) {
console.log("Success:", result.value);
} else {
console.error("Failure:", result.value.message);
}β Creating a success result
const result = Result.success("Data loaded!");
if (result.isOk()) {
console.log(result.value); // "Data loaded!"
}β Creating an error result
const errorResult = Result.error(new Error("Failed to load"));
if (errorResult.isFail()) {
console.error(errorResult.value.message); // "Failed to load"
}β orElseThrow - value or throw
const data = result.orElseThrow(); // Returns the value
const error = errorResult.orElseThrow(); // Throws the errorWith custom transformation:
errorResult.orElseThrow((err) => `Something went wrong: ${err.message}`);
// Throws: Error("Something went wrong: Failed to load")β orElse - value or fallback
const fallback = errorResult.orElse("default");
console.log(fallback); // "default"β map - transform successful value
const upper = result.map((str) => str.toUpperCase());
console.log(upper.value); // "DATA LOADED!"If transformation throws:
const risky = result.map(() => {
throw new Error("Boom!");
});
console.log(risky.isFail()); // trueβ validate - enforce conditions
const valid = result.validate(val => val.length > 0, new Error("Empty string"));Wrap any promise into an AsyncResult:
const promise = fetch("/api/data").then(res => res.json());
const asyncRes = new AsyncResult(promise);β Check status
const ok = await asyncRes.isOkAsync();
const fail = await asyncRes.isFailAsync();β Get resolved value, error or result
const result = await asyncRes.getResultAsync(); // T | E
const value = await asyncRes.getValueAsync(); // T | null
const error = await asyncRes.getErrorAsync(); // E | nullβ orElseAsync
const fallbackData = await asyncRes.orElseAsync({ default: true });β orElseThrowAsync
const data = await asyncRes.orElseThrowAsync();With transformation:
await asyncRes.orElseThrowAsync((err) => new Error("Custom: " + err.message));β waitAsync
await asyncRes.waitAsync(); // Does not throw on failureβ map async value
const transformed = asyncRes.map(data => data.items.length);β validate async result
const validated = asyncRes.validate(data => data.items.length > 0, new Error("No items found"));β resolveAsync to Result
const syncResult = await asyncRes.resolveAsync();
if (syncResult.isOk()) {
console.log(syncResult.value);
}