停止抛出异常。改用 Option 和 Result。
Source: Dev.to
请提供您希望翻译的文章正文(除代码块、URL 之外的文字),我将按照要求把它翻译成简体中文并保持原有的 Markdown 格式。
JavaScript 错误处理的问题
function getUser(id: number): User | null {
// ...
}
调用者必须记得进行 null 检查。类型系统会提示他们,但没有任何东西能阻止这种情况:
const user = getUser(42);
console.log(user.name); // 如果 user 为 null,运行时会抛出 TypeError
更糟糕的模式是将可能的失败隐藏在 async 函数中:
async function fetchConfig(): Promise {
// can throw network error, parse error, validation error...
// none of these appear in the type signature
}
这些错误是不可见的,导致调用者不知道需要处理什么。
介绍 TypeScript 中的 Option 与 Result
Rust 的 Option 和 Result 在类型签名中显式地表示可能的缺失或失败。@rslike/std 将同样的思想引入了 TypeScript。
npm i @rslike/std
使用 Option
import { Some, None, Option, match } from "@rslike/std";
function findUser(id: number): Option {
const user = db.find(u => u.id === id);
return user ? Some(user) : None();
}
安全提取
const opt = findUser(42);
// Fallback value
const user = opt.unwrapOr(guestUser);
模式匹配
const greeting = match(
opt,
(user) => `Hello, ${user.name}!`,
() => "Hello, guest!"
);
转换
const displayName = findUser(42)
.map(u => `${u.firstName} ${u.lastName}`)
.unwrapOr("Unknown User");
链式调用 Options
const avatar = findUser(42)
.flatMap(u => findAvatar(u.avatarId))
.unwrapOr(defaultAvatar);
基础 API
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!
使用 Result
import { Ok, Err, Result, match } from "@rslike/std";
function divide(a: number, b: number): Result {
return new Result((ok, err) => {
if (b === 0) {
err("Division by zero");
} else {
ok(a / b);
}
});
}
检查 Result
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
使用 Result 解析 JSON
function parseJSON(raw: string): Result {
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);
对 Result 进行匹配
const message = match(
parseJSON(rawInput),
(data) => `Loaded: ${JSON.stringify(data)}`,
(err) => `Error: ${err.message}`
);
match 实用工具
match 支持布尔值、Option 和 Result,提供穷尽的双分支分派。
import { match } from "@rslike/std";
// Boolean
match(isAdmin, (t) => "admin panel", (f) => "dashboard");
// Option
match(someOption, (value) => `Got: ${value}`, () => "nothing");
// Result
match(someResult, (n) => n * 2, (e) => -1);
TypeScript 会根据输入推断回调参数的类型,防止将错误处理器误用为成功处理器。
全局导入(可选)
// 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"));
代码审查的好处
当 Option 和 Result 出现在函数签名中时,讨论从 “我们忘记处理 null/异常了吗?” 转变为 “预期的契约是什么?”
| Before | After |
|---|---|
function getSession(token: string): Session | null | function getSession(token: string): Option |
async function createOrder(cart: Cart): Promise | async function createOrder(cart: Cart): Promise> |
安装
npm i @rslike/std