Tools
Tools: Stop Throwing Exceptions. Use Option and Result Instead.
2026-02-20
0 views
admin
There's a better model ## Option — make absence impossible to ignore ## Transform without unwrapping ## Quick checks ## Result — make errors part of the contract ## Wrapping existing code that throws ## Pattern matching ## match — exhaustive two-branch dispatch ## Globals — skip the imports ## The real benefit ## Install Let's talk about what's wrong with JavaScript error handling. Here's a function: The caller has to remember to null-check. The type system nudges them, but there's nothing stopping this: Or this pattern, which is even worse: The errors are invisible. The caller doesn't know what to handle. Rust has Option<T> for values that might not exist, and Result<T, E> for operations that can fail. Both are explicit in the type signature. Both force the caller to handle every case. @rslike/std brings this to TypeScript. Now the caller must handle both states. They can't accidentally treat None as a value: The error type is in the signature. Callers know what to expect: match works with Option, Result, and boolean: TypeScript infers the callback parameter types from the input — you can't accidentally use the error handler as the success handler. When Option and Result are in your function signatures, code review becomes a conversation about intent rather than a hunt for unhandled edge cases. Source: github.com/vitalics/rslike Have you used Option/Result patterns in TypeScript before? What library are you using? Let me know below. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
function getUser(id: number): User | null { // ...
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
function getUser(id: number): User | null { // ...
} CODE_BLOCK:
function getUser(id: number): User | null { // ...
} CODE_BLOCK:
const user = getUser(42);
console.log(user.name); // TypeError at runtime if user is null Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
const user = getUser(42);
console.log(user.name); // TypeError at runtime if user is null CODE_BLOCK:
const user = getUser(42);
console.log(user.name); // TypeError at runtime if user is null COMMAND_BLOCK:
async function fetchConfig(): Promise<Config> { // can throw network error, parse error, validation error... // none of these appear in the type signature
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
async function fetchConfig(): Promise<Config> { // can throw network error, parse error, validation error... // none of these appear in the type signature
} COMMAND_BLOCK:
async function fetchConfig(): Promise<Config> { // can throw network error, parse error, validation error... // none of these appear in the type signature
} COMMAND_BLOCK:
npm i @rslike/std Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
npm i @rslike/std COMMAND_BLOCK:
npm i @rslike/std COMMAND_BLOCK:
import { Some, None, Option, match } from "@rslike/std"; function findUser(id: number): Option<User> { const user = db.find(u => u.id === id); return user ? Some(user) : None();
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
import { Some, None, Option, match } from "@rslike/std"; function findUser(id: number): Option<User> { const user = db.find(u => u.id === id); return user ? Some(user) : None();
} COMMAND_BLOCK:
import { Some, None, Option, match } from "@rslike/std"; function findUser(id: number): Option<User> { const user = db.find(u => u.id === id); return user ? Some(user) : None();
} COMMAND_BLOCK:
const opt = findUser(42); // Safe extraction with fallback
const user = opt.unwrapOr(guestUser); // Pattern matching — handles both branches exhaustively
const greeting = match( opt, (user) => `Hello, ${user.name}!`, () => "Hello, guest!"
); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
const opt = findUser(42); // Safe extraction with fallback
const user = opt.unwrapOr(guestUser); // Pattern matching — handles both branches exhaustively
const greeting = match( opt, (user) => `Hello, ${user.name}!`, () => "Hello, guest!"
); COMMAND_BLOCK:
const opt = findUser(42); // Safe extraction with fallback
const user = opt.unwrapOr(guestUser); // Pattern matching — handles both branches exhaustively
const greeting = match( opt, (user) => `Hello, ${user.name}!`, () => "Hello, guest!"
); COMMAND_BLOCK:
const displayName = findUser(42) .map(u => `${u.firstName} ${u.lastName}`) .unwrapOr("Unknown User"); // Chain operations that also return Option
const avatar = findUser(42) .flatMap(u => findAvatar(u.avatarId)) .unwrapOr(defaultAvatar); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
const displayName = findUser(42) .map(u => `${u.firstName} ${u.lastName}`) .unwrapOr("Unknown User"); // Chain operations that also return Option
const avatar = findUser(42) .flatMap(u => findAvatar(u.avatarId)) .unwrapOr(defaultAvatar); COMMAND_BLOCK:
const displayName = findUser(42) .map(u => `${u.firstName} ${u.lastName}`) .unwrapOr("Unknown User"); // Chain operations that also return Option
const avatar = findUser(42) .flatMap(u => findAvatar(u.avatarId)) .unwrapOr(defaultAvatar); CODE_BLOCK:
const opt = Some("hello"); opt.isSome(); // true
opt.isNone(); // false
opt.unwrap(); // "hello" const empty = None();
empty.isNone(); // true
empty.unwrapOr("fallback"); // "fallback"
empty.unwrap(); // throws UndefinedBehaviorError — intentional! Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
const opt = Some("hello"); opt.isSome(); // true
opt.isNone(); // false
opt.unwrap(); // "hello" const empty = None();
empty.isNone(); // true
empty.unwrapOr("fallback"); // "fallback"
empty.unwrap(); // throws UndefinedBehaviorError — intentional! CODE_BLOCK:
const opt = Some("hello"); opt.isSome(); // true
opt.isNone(); // false
opt.unwrap(); // "hello" const empty = None();
empty.isNone(); // true
empty.unwrapOr("fallback"); // "fallback"
empty.unwrap(); // throws UndefinedBehaviorError — intentional! COMMAND_BLOCK:
import { Ok, Err, Result, match } from "@rslike/std"; function divide(a: number, b: number): Result<number, string> { return new Result((ok, err) => { if (b === 0) { err("Division by zero"); } else { ok(a / b); } });
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
import { Ok, Err, Result, match } from "@rslike/std"; function divide(a: number, b: number): Result<number, string> { return new Result((ok, err) => { if (b === 0) { err("Division by zero"); } else { ok(a / b); } });
} COMMAND_BLOCK:
import { Ok, Err, Result, match } from "@rslike/std"; function divide(a: number, b: number): Result<number, string> { return new Result((ok, err) => { if (b === 0) { err("Division by zero"); } else { ok(a / b); } });
} CODE_BLOCK:
const r = divide(10, 2); r.isOk(); // true
r.unwrap(); // 5 const bad = divide(10, 0);
bad.isErr(); // true
bad.unwrapErr(); // "Division by zero"
bad.unwrapOr(0); // 0 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
const r = divide(10, 2); r.isOk(); // true
r.unwrap(); // 5 const bad = divide(10, 0);
bad.isErr(); // true
bad.unwrapErr(); // "Division by zero"
bad.unwrapOr(0); // 0 CODE_BLOCK:
const r = divide(10, 2); r.isOk(); // true
r.unwrap(); // 5 const bad = divide(10, 0);
bad.isErr(); // true
bad.unwrapErr(); // "Division by zero"
bad.unwrapOr(0); // 0 COMMAND_BLOCK:
function parseJSON(raw: string): Result<unknown, SyntaxError> { return new Result((ok, err) => { try { ok(JSON.parse(raw)); } catch (e) { err(e as SyntaxError); } });
} const config = parseJSON(rawInput) .map(data => validate(data)) .mapErr(e => `Invalid config: ${e.message}`) .unwrapOr(defaults); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
function parseJSON(raw: string): Result<unknown, SyntaxError> { return new Result((ok, err) => { try { ok(JSON.parse(raw)); } catch (e) { err(e as SyntaxError); } });
} const config = parseJSON(rawInput) .map(data => validate(data)) .mapErr(e => `Invalid config: ${e.message}`) .unwrapOr(defaults); COMMAND_BLOCK:
function parseJSON(raw: string): Result<unknown, SyntaxError> { return new Result((ok, err) => { try { ok(JSON.parse(raw)); } catch (e) { err(e as SyntaxError); } });
} const config = parseJSON(rawInput) .map(data => validate(data)) .mapErr(e => `Invalid config: ${e.message}`) .unwrapOr(defaults); COMMAND_BLOCK:
const message = match( parseJSON(rawInput), (data) => `Loaded: ${JSON.stringify(data)}`, (err) => `Error: ${err.message}`
); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
const message = match( parseJSON(rawInput), (data) => `Loaded: ${JSON.stringify(data)}`, (err) => `Error: ${err.message}`
); COMMAND_BLOCK:
const message = match( parseJSON(rawInput), (data) => `Loaded: ${JSON.stringify(data)}`, (err) => `Error: ${err.message}`
); COMMAND_BLOCK:
import { match } from "@rslike/std"; // boolean
match(isAdmin, (t) => "admin panel", (f) => "dashboard"); // Option<string>
match(someOption, (value) => `Got: ${value}`, () => "nothing"); // Result<number, Error>
match(someResult, (n) => n * 2, (e) => -1); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
import { match } from "@rslike/std"; // boolean
match(isAdmin, (t) => "admin panel", (f) => "dashboard"); // Option<string>
match(someOption, (value) => `Got: ${value}`, () => "nothing"); // Result<number, Error>
match(someResult, (n) => n * 2, (e) => -1); COMMAND_BLOCK:
import { match } from "@rslike/std"; // boolean
match(isAdmin, (t) => "admin panel", (f) => "dashboard"); // Option<string>
match(someOption, (value) => `Got: ${value}`, () => "nothing"); // Result<number, Error>
match(someResult, (n) => n * 2, (e) => -1); CODE_BLOCK:
// entry.ts — once
import "@rslike/std/globals"; // anywhere else in your app — no imports needed
const x = Some(42);
const r = Ok("success");
const n = None();
const e = Err(new Error("oops")); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
// entry.ts — once
import "@rslike/std/globals"; // anywhere else in your app — no imports needed
const x = Some(42);
const r = Ok("success");
const n = None();
const e = Err(new Error("oops")); CODE_BLOCK:
// entry.ts — once
import "@rslike/std/globals"; // anywhere else in your app — no imports needed
const x = Some(42);
const r = Ok("success");
const n = None();
const e = Err(new Error("oops")); CODE_BLOCK:
// Before: what does null mean here? forgotten value? intentional absence?
function getSession(token: string): Session | null // After: clear contract — either a session or nothing
function getSession(token: string): Option<Session> // Before: can this throw? which errors?
async function createOrder(cart: Cart): Promise<Order> // After: explicit failure type in the signature
async function createOrder(cart: Cart): Promise<Result<Order, OrderError>> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
// Before: what does null mean here? forgotten value? intentional absence?
function getSession(token: string): Session | null // After: clear contract — either a session or nothing
function getSession(token: string): Option<Session> // Before: can this throw? which errors?
async function createOrder(cart: Cart): Promise<Order> // After: explicit failure type in the signature
async function createOrder(cart: Cart): Promise<Result<Order, OrderError>> CODE_BLOCK:
// Before: what does null mean here? forgotten value? intentional absence?
function getSession(token: string): Session | null // After: clear contract — either a session or nothing
function getSession(token: string): Option<Session> // Before: can this throw? which errors?
async function createOrder(cart: Cart): Promise<Order> // After: explicit failure type in the signature
async function createOrder(cart: Cart): Promise<Result<Order, OrderError>> COMMAND_BLOCK:
npm i @rslike/std Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
npm i @rslike/std COMMAND_BLOCK:
npm i @rslike/std
how-totutorialguidedev.toainetworkjavascriptgitgithub