better-result-adopt
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesebetter-result Adoption
better-result 迁移指南
Migrate existing error handling (try/catch, Promise rejections, thrown exceptions) to typed Result-based error handling with better-result.
将现有的错误处理(try/catch、Promise拒绝、抛出异常)迁移至使用better-result的类型化Result式错误处理。
When to Use
适用场景
- Adopting better-result in existing codebase
- Converting try/catch blocks to Result types
- Replacing thrown exceptions with typed errors
- Migrating Promise-based code to Result.tryPromise
- Introducing railway-oriented programming patterns
- 在现有代码库中采用better-result
- 将try/catch块转换为Result类型
- 用类型化错误替代抛出的异常
- 将基于Promise的代码迁移至Result.tryPromise
- 引入面向铁路编程模式
Migration Strategy
迁移策略
1. Start at Boundaries
1. 从边界开始
Begin migration at I/O boundaries (API calls, DB queries, file ops) and work inward. Don't attempt full-codebase migration at once.
从I/O边界(API调用、数据库查询、文件操作)开始迁移,逐步向内推进。不要尝试一次性完成整个代码库的迁移。
2. Identify Error Categories
2. 识别错误类别
Before migrating, categorize errors in target code:
| Category | Example | Migration Target |
|---|---|---|
| Domain errors | NotFound, Validation | TaggedError + Result.err |
| Infrastructure | Network, DB connection | Result.tryPromise + TaggedError |
| Bugs/defects | null deref, type error | Let throw (becomes Panic if in Result callback) |
迁移前,对目标代码中的错误进行分类:
| 类别 | 示例 | 迁移目标 |
|---|---|---|
| 领域错误 | NotFound、Validation | TaggedError + Result.err |
| 基础设施错误 | 网络问题、数据库连接失败 | Result.tryPromise + TaggedError |
| 漏洞/缺陷 | 空引用、类型错误 | 允许抛出(在Result回调中会成为Panic) |
3. Migration Order
3. 迁移顺序
- Define TaggedError classes for domain errors
- Wrap throwing functions with Result.try/tryPromise
- Convert imperative error checks to Result chains
- Refactor callbacks to generator composition
- 为领域错误定义TaggedError类
- 用Result.try/tryPromise包装抛出异常的函数
- 将命令式错误检查转换为Result链式调用
- 将回调重构为生成器组合
Pattern Transformations
模式转换
Try/Catch to Result.try
Try/Catch 转换为 Result.try
typescript
// BEFORE
function parseConfig(json: string): Config {
try {
return JSON.parse(json);
} catch (e) {
throw new ParseError(e);
}
}
// AFTER
function parseConfig(json: string): Result<Config, ParseError> {
return Result.try({
try: () => JSON.parse(json) as Config,
catch: (e) => new ParseError({ cause: e, message: `Parse failed: ${e}` }),
});
}typescript
// BEFORE
function parseConfig(json: string): Config {
try {
return JSON.parse(json);
} catch (e) {
throw new ParseError(e);
}
}
// AFTER
function parseConfig(json: string): Result<Config, ParseError> {
return Result.try({
try: () => JSON.parse(json) as Config,
catch: (e) => new ParseError({ cause: e, message: `Parse failed: ${e}` }),
});
}Async/Await to Result.tryPromise
Async/Await 转换为 Result.tryPromise
typescript
// BEFORE
async function fetchUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new ApiError(res.status);
return res.json();
}
// AFTER
async function fetchUser(id: string): Promise<Result<User, ApiError | UnhandledException>> {
return Result.tryPromise({
try: async () => {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new ApiError({ status: res.status, message: `API ${res.status}` });
return res.json() as Promise<User>;
},
catch: (e) => (e instanceof ApiError ? e : new UnhandledException({ cause: e })),
});
}typescript
// BEFORE
async function fetchUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new ApiError(res.status);
return res.json();
}
// AFTER
async function fetchUser(id: string): Promise<Result<User, ApiError | UnhandledException>> {
return Result.tryPromise({
try: async () => {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new ApiError({ status: res.status, message: `API ${res.status}` });
return res.json() as Promise<User>;
},
catch: (e) => (e instanceof ApiError ? e : new UnhandledException({ cause: e })),
});
}Null Checks to Result
空值检查转换为Result
typescript
// BEFORE
function findUser(id: string): User | null {
return users.find((u) => u.id === id) ?? null;
}
// Caller must check: if (user === null) ...
// AFTER
function findUser(id: string): Result<User, NotFoundError> {
const user = users.find((u) => u.id === id);
return user
? Result.ok(user)
: Result.err(new NotFoundError({ id, message: `User ${id} not found` }));
}
// Caller: yield* findUser(id) in Result.gen, or .match()typescript
// BEFORE
function findUser(id: string): User | null {
return users.find((u) => u.id === id) ?? null;
}
// 调用者必须检查: if (user === null) ...
// AFTER
function findUser(id: string): Result<User, NotFoundError> {
const user = users.find((u) => u.id === id);
return user
? Result.ok(user)
: Result.err(new NotFoundError({ id, message: `User ${id} not found` }));
}
// 调用者: 在Result.gen中使用yield* findUser(id),或使用.match()Callback Hell to Generator
回调地狱转换为生成器
typescript
// BEFORE
async function processOrder(orderId: string) {
try {
const order = await fetchOrder(orderId);
if (!order) throw new NotFoundError(orderId);
const validated = validateOrder(order);
if (!validated.ok) throw new ValidationError(validated.errors);
const result = await submitOrder(validated.data);
return result;
} catch (e) {
if (e instanceof NotFoundError) return { error: "not_found" };
if (e instanceof ValidationError) return { error: "invalid" };
throw e;
}
}
// AFTER
async function processOrder(orderId: string): Promise<Result<OrderResult, OrderError>> {
return Result.gen(async function* () {
const order = yield* Result.await(fetchOrder(orderId));
const validated = yield* validateOrder(order);
const result = yield* Result.await(submitOrder(validated));
return Result.ok(result);
});
}
// Error type is union of all yielded errorstypescript
// BEFORE
async function processOrder(orderId: string) {
try {
const order = await fetchOrder(orderId);
if (!order) throw new NotFoundError(orderId);
const validated = validateOrder(order);
if (!validated.ok) throw new ValidationError(validated.errors);
const result = await submitOrder(validated.data);
return result;
} catch (e) {
if (e instanceof NotFoundError) return { error: "not_found" };
if (e instanceof ValidationError) return { error: "invalid" };
throw e;
}
}
// AFTER
async function processOrder(orderId: string): Promise<Result<OrderResult, OrderError>> {
return Result.gen(async function* () {
const order = yield* Result.await(fetchOrder(orderId));
const validated = yield* validateOrder(order);
const result = yield* Result.await(submitOrder(validated));
return Result.ok(result);
});
}
// 错误类型是所有yield错误的联合类型Defining TaggedErrors
定义TaggedErrors
See references/tagged-errors.md for TaggedError patterns.
有关TaggedError模式,请参阅references/tagged-errors.md。
Workflow
工作流程
- Check for source reference: Look for directory - if present, read the better-result source code for implementation details and patterns
opensrc/ - Audit: Find try/catch, Promise.catch, thrown errors in target module
- Define errors: Create TaggedError classes for domain errors
- Wrap boundaries: Use Result.try/tryPromise at I/O points
- Chain operations: Convert if/else error checks to .andThen or Result.gen
- Update signatures: Change return types to Result<T, E>
- Update callers: Propagate Result handling up call stack
- Test: Verify error paths with .match or type narrowing
- 检查源引用:查看是否存在目录——如果存在,阅读better-result的源代码以了解实现细节和模式
opensrc/ - 审计:在目标模块中查找try/catch、Promise.catch、抛出的错误
- 定义错误:为领域错误创建TaggedError类
- 包装边界:在I/O点使用Result.try/tryPromise
- 链式操作:将if/else错误检查转换为.andThen或Result.gen
- 更新签名:将返回类型更改为Result<T, E>
- 更新调用者:在调用栈中向上传播Result处理逻辑
- 测试:使用.match或类型收窄验证错误路径
Common Pitfalls
常见陷阱
- Over-wrapping: Don't wrap every function. Start at boundaries, propagate inward.
- Losing error info: Always include cause/context in TaggedError constructors.
- Mixing paradigms: Once a module returns Result, callers should too (or explicitly .unwrap).
- Ignoring Panic: Callbacks that throw become Panic. Fix the bug, don't catch Panic.
- 过度包装:不要包装每个函数。从边界开始,逐步向内传播。
- 丢失错误信息:始终在TaggedError构造函数中包含原因/上下文。
- 混合范式:一旦模块返回Result,调用者也应返回Result(或显式调用.unwrap)。
- 忽略Panic:抛出异常的回调会成为Panic。修复漏洞,不要捕获Panic。
References
参考资料
- TaggedError Patterns - Defining and matching typed errors
- directory (if present) - Full better-result source code for deeper context
opensrc/
- TaggedError Patterns - 定义和匹配类型化错误
- 目录(如果存在)- 完整的better-result源代码,提供更深入的上下文
opensrc/