Rust's Option and Result in TypeScript — Without the Boilerplate

posted 2 min read

JavaScript error handling is a mess. You either throw exceptions that are invisible in type signatures, or return null | T and forget to check, or build your own { ok: boolean, data: T, error: E } shape — inconsistently, every time.

Rust has a better model. Option<T> and Result<T, E> are types that make absence and failure explicit. @rslike/std brings them to TypeScript.

npm i @rslike/std

Option<T> — values that might not exist

Instead of T | null | undefined, use Option:

import { Some, None, Option, match } from "@rslike/std";

function findUser(id: number): Option<User> {
  const user = db.find(id);
  return user ? Some(user) : None();
}

const result = findUser(42);

match(
  result,
  (user) => console.log("Found:", user.name),
  ()     => console.log("Not found")
);

Some(value) wraps a present value. None() represents absence. The type system forces you to handle both.

Chaining operations

const name = findUser(42)
  .map(u => u.name)
  .unwrapOr("Anonymous");

// flatMap for operations that also return Option
const email = findUser(42)
  .flatMap(u => findEmail(u.id))
  .unwrapOr("*Emails are not allowed*");

Checking status

const opt = Some(123);

opt.isSome();   // true
opt.isNone();   // false
opt.unwrap();   // 123
opt.unwrapOr(0); // 123

None().unwrapOr(0); // 0
None().unwrap();    // throws UndefinedBehaviorError

Result<T, E> — operations that can fail

Instead of try/catch scattered everywhere:

import { Ok, Err, Result, match } from "@rslike/std";

function parseJSON(raw: string): Result<unknown, SyntaxError> {
  return new Result((ok, err) => {
    try {
      ok(JSON.parse(raw));
    } catch (e) {
      err(e as SyntaxError);
    }
  });
}

const r = parseJSON('{"valid": true}');

match(
  r,
  (data) => console.log("Parsed:", data),
  (err)  => console.log("Failed:", err.message)
);

Chaining results

const config = parseJSON(rawInput)
  .map(data => validate(data))
  .mapErr(e => new ConfigError(e.message))
  .unwrapOr(defaultConfig);

Combining with Option

import { Ok, Err, Some, None, match } from "@rslike/std";

function getPort(config: Result<Config, Error>): number {
  return match(
    config,
    (cfg) => match(
      cfg.port ? Some(cfg.port) : None(),
      (p) => p,
      ()  => 3000
    ),
    () => 3000
  );
}

match — pattern matching for Option and Result

match is a typed two-branch function that handles both states exhaustively:

import { match } from "@rslike/std";

// With Option
match(someOption, (value) => ..., () => ...);

// With Result
match(someResult, (value) => ..., (error) => ...);

// With boolean
match(flag, (t) => ..., (f) => ...);

The callback types are inferred from the input type — you can't mix up Ok and Err handlers.

Globals

For convenience, import globals once in your entry file to make Some, None, Ok, Err available without imports everywhere:

// entry.ts
import "@rslike/std/globals";

// any other file
const x = Some(42);      // works without import
const r = Ok("success"); // works without import

Why not just throw?

  • Thrown errors don't appear in function signatures — callers don't know what can fail
  • try/catch is verbose and easy to forget
  • null checks are easy to skip
  • Option and Result make the happy path and the failure path equally explicit

Install

npm i @rslike/std

Part of the rslike monorepo.

2 Comments

1 vote
1 vote

More Posts

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13

Google Drive Sync

Pocket Portfolioverified - Jan 5

Beyond the 98.6°F Myth: Defining Personal Baselines in Health Management

Huifer - Feb 2

Just completed another large-scale WordPress migration — and the client left this

saqib_devmorph - Apr 7
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!