A TypeScript library inspired by Rust, providing Result and Option types for
safe error handling and null management with functional programming patterns.
JavaScript/TypeScript error handling often relies on try...catch blocks or
nullable return types, which can be verbose or hide potential errors. rustify
brings Rust-inspired monads like Result and Option to TypeScript, enabling
functional programming patterns for safer code. This allows you to:
-
Handle errors explicitly: Functions return a
Resultwhich is eitherOk(value)for success orErr(error)for failure. -
Manage nullable values safely: Use
Optionto represent values that may or may not exist, eliminating null/undefined errors. -
Improve type safety: Both
Result<T, E>andOption<T>types are tracked by the type system. -
Chain operations safely: Monadic methods like
andThen,map, andorElseallow elegant functional composition. -
Perform exhaustive checks: The
matchmethod ensures you handle all cases -
Enforce deep immutability: Use
Immutable<T>to recursively make all propertiesreadonlyand convert mutable collections (Array,Map,Set) to their readonly counterparts.explicitly.
-
Easily wrap unsafe functions:
Result.fromandOption.fromNullableprovide simple ways to convert potentially unsafe operations. -
Destructure results easily: Use
asTuple()for Go-style[err, val]destructuring, orasObject()if you prefer{ error, value }destructuring.
You can install rustify using your favorite package manager or directly from
jsr.
npm:
npm install @ghaerdi/rustifyjsr:
npx jsr add @ghaerdi/rustifyimport { Err, Ok, Result } from "@ghaerdi/rustify";
import { match } from "@ghaerdi/rustify/match";
function divide(
numerator: number,
denominator: number,
): Result<number, string> {
if (denominator === 0) return Err("Cannot divide by zero");
return Ok(numerator / denominator);
}
// match() handles both variants exhaustively — the Ok value and the error are
// narrowed to their types, so (v) is number and (e) is string.
const message = match(divide(10, 2))
.with(Result.ok, (v) => `Result: ${v}`)
.with(Result.err, (e) => `Error: ${e}`)
.exhaustive();
console.log(message); // "Result: 5"Wrap throwing/rejecting code (like JSON.parse or an HTTP call) in
Result.from / Result.fromAsync to turn exceptions into Err values:
import { match } from "@ghaerdi/rustify/match";
import { Result } from "@ghaerdi/rustify";
// sync — catches any thrown error into Err
const parsed = Result.from(() => JSON.parse('{"x": 1}'));
console.log(
match(parsed)
.with(Result.ok, (v) => `parsed: ${JSON.stringify(v)}`) // "parsed: {"x":1}"
.with(Result.err, (e) => `error: ${e}`)
.exhaustive(),
);
// async — awaits and catches both sync throws and rejected promises
const body = await Result.fromAsync(async () =>
(await fetch("/api/user")).json()
);
if (body.isOk()) console.log(body.unwrap());Use Immutable<T> to recursively make all properties readonly and convert
mutable collections to their readonly counterparts:
import type { Immutable } from "@ghaerdi/rustify";
type User = {
name: string;
tags: string[];
metadata: { role: string };
};
type FrozenUser = Immutable<User>;
// {
// readonly name: string;
// readonly tags: readonly string[];
// readonly metadata: { readonly role: string };
// }
const user: Immutable<User> = {
name: "Alice",
tags: ["admin"],
metadata: { role: "admin" },
};
// user.name = "Bob"; // ❌ Error: readonly property
// user.tags.push("mod"); // ❌ Error: readonly array
// user.metadata.role = "user"; // ❌ Error: nested readonly propertyBuilt-in types like Date, RegExp, Promise, WeakMap, and WeakSet pass
through unchanged since readonly properties cannot prevent method-based
mutation.
Full API reference, match() guide, and worked examples live in the
library wiki:
- Result — full
Result<T, E>API. - Option — full
Option<T>API. - match — type-safe pattern matching.
- Examples — worked examples.
This project uses Deno (>= 2.x). No install step is required for tests or checks
— dependencies are resolved from JSR via the lockfile. The npm build
(deno task build:npm) pulls esbuild from npm, auto-installed via
"nodeModulesDir": "auto" in deno.json.
-
Dev Environment (optional): a
devenvshell (devenv.nix) with git-hooks is available —devenv testvalidates the environment (hooks + type check),devenv shellenters it. -
Formatting:
deno fmt
-
Linting:
deno lint
-
Type Checking:
deno task check
-
Run Tests:
deno test
Contributions welcome! Please submit issues and pull requests.
- Fork the repository.
- Create your feature branch.
- Commit your changes.
- Push to the branch.
- Open a Pull Request.
MIT License - see the LICENSE file for details.