Tools: Stop Throwing Exceptions. Use Option and Result Instead.

Tools: Stop Throwing Exceptions. Use Option and Result Instead.

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. 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 ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or CODE_BLOCK: function getUser(id: number): User | null { // ... } 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 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 } 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 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(); } 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!" ); 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); 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! 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); } }); } 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 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); 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}` ); 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); 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")); 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>> 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 COMMAND_BLOCK: npm i @rslike/std COMMAND_BLOCK: npm i @rslike/std