@mkvlrn/result
v5.0.8
Published
Simple Result type/pattern for TypeScript
Readme
@mkvlrn/result
Dead simple Result pattern for TypeScript.
No .map(), no .flatMap(), no .andThen(), no .orElse(), no .unwrap(), no monadic gymnastics. Just a type, two functions, and TypeScript doing what TypeScript already does well.
Why this one?
There are dozens of Result libraries for TypeScript. Nearly all of them bolt on method chaining, transformation pipelines, and functional programming utilities that turn a simple concept into an entire paradigm.
This package does one thing: gives you a type-safe Result<T, E> discriminated union with ok() and err() constructors. You use if/else to handle it. TypeScript narrows the type for you. That's the whole API.
The entire implementation is ~35 lines. Zero runtime dependencies. Four exports.
If you need .map().flatMap().andThen().orElse().unwrapOr() chains, use neverthrow or ts-results. They're good libraries. This isn't that.
Installation
pnpm add @mkvlrn/resultAPI
| Export | What it does |
| ------------------- | -------------------------------------------- |
| Result<T, E> | Union type representing success or failure |
| AsyncResult<T, E> | Promise<Result<T, E>> for async operations |
| ok(value) | Creates a success result |
| err(error) | Creates an error result |
That's it. That's the whole thing.
Usage
import { type Result, type AsyncResult, ok, err } from "@mkvlrn/result";Create results, check results
function divide(a: number, b: number): Result<number, Error> {
if (b === 0) {
return err(new Error("Division by zero"));
}
return ok(a / b);
}
const result = divide(10, 2);
if (result.isOk) {
console.log(result.value); // number - TypeScript knows
}
if (result.isError) {
console.log(result.error.message); // Error - TypeScript knows
}No .unwrap(). No .expect(). Just an if statement and the compiler handles the rest.
Async Operations
async function fetchUser(id: number): AsyncResult<User, Error> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return err(new Error(`HTTP ${response.status}`));
}
const user = await response.json();
return ok(user);
} catch (error) {
return err(error instanceof Error ? error : new Error("Unknown error"));
}
}AsyncResult<T, E> is just Promise<Result<T, E>>. It's a type alias, not a class, not a wrapper, not a monad.
Custom Error Types
class ValidationError extends Error {
readonly code: number;
constructor(code: number, message: string) {
super(message);
this.name = "ValidationError";
this.code = code;
}
}
function validateEmail(email: string): Result<string, ValidationError> {
if (!email.includes("@")) {
return err(new ValidationError(400, "bad-email"));
}
return ok(email);
}
const result = validateEmail("invalid-email");
if (result.isError) {
// TypeScript knows this is a ValidationError, not just Error
console.log(`${result.error.code}: ${result.error.message}`); // 400: bad-email
}License
MIT
