A minimal functional utility library for TypeScript, inspired by Rust's Result and Option. It provides safe, composable handling of errors and optional values.
Result<T>– represents a computation that may succeed (ok) or fail (err).Option<T>/Maybe<T>– represents a value that may exist (Some) or be absent (None).SafePromise<T>– wraps async computations, converting exceptions intoResult<T>.- Supports chainable operations:
map,andThen,filter,zip,transpose,mapErr,or,orElse,unwrap,expect,inspect.
Goals:
- Explore functional programming in TypeScript.
- Provide a small, dependency-free, composable monads toolkit.
- Enable safe handling of errors and optional values without exceptions.
Status: eperimental. API may change. contributions welcome.
bun add @nand.computer/monadsimport { ok, err, Some, None, wrap, todo } from "@nand.computer/monads";Result<T> is either:
ok(value: T)– successerr(message: string)– failure
.map(fn)– transform success value.andThen(fn)– chain computations.mapErr(fn)– transform the error.filter(predicate, errMsg)– fail if predicate is false.zip(otherResult)– combine two results.transpose()– convertResult<Option<T>>→Option<Result<T>>.unwrap()– return value or throw.expect(msg)– return value or throw with custom message.ok()– returns the value ornull.err()– returns the error message ornull
Option<T> represents an optional value:
Some(value)– value existsNone()– value absent
.map(fn)– transform value.andThen(fn)– chain computations.unwrap()– return value or throw.expect(msg)– return value or throw with message.filter(predicate)– convert toNoneif predicate fails
Wraps async functions to automatically return Result<T>:
const safe = await wrap(async () => 10 * 2);
console.log(safe.ok()); // 20// Basic map/andThen/mapErr
const r1 = ok(10)
.map((x) => x * 2)
.andThen((x) => (x > 15 ? ok(x) : err<number>("Too small")))
.mapErr((e) => `Error happened: ${e}`);
console.log(r1.ok(), r1.err()); // 20, null
// Filter
const filtered = ok(100).filter((x) => x > 50, "Less than 50");
console.log(filtered.ok(), filtered.err()); // 100, null
// Zip
const zipped = ok(10).zip(ok("hello"));
console.log(zipped.ok()); // [10, "hello"]
// Option + transpose
const rOpt: Result<Option<number>> = ok(Some(5));
const transposed = rOpt.transpose();
console.log(transposed.isSome ? transposed.value!.ok() : null); // 5
const rOptNone: Result<Option<number>> = ok(None());
const transposedNone = rOptNone.transpose();
console.log(transposedNone.isNone); // true
// SafePromise / wrap
const asyncFunc = async (x: number) =>
x > 0
? x * 2
: (() => {
throw new Error("Negative");
})();
const safe = await wrap(() => asyncFunc(5));
console.log(safe.ok()); // 10
const safeErr = await wrap(() => asyncFunc(-1));
console.log(safeErr.err()); // "Unexpected error"
// Complex chaining
const complex = await wrap(async () => 20).then((r) =>
r
.filter((x) => x > 10, "Too small")
.andThen((x) => ok(x * 3))
.zip(ok(50))
.map(([a, b]) => a + b),
);
console.log(complex.ok()); // 110- Learn, experiment, and have fun with functional patterns.
- Provide a safe, composable substrate for async and optional computations.
- Maintain simplicity and extensibility.