关于 better-result-adopt
better-result Adopt 是一项技能,用于在现有代码库中逐步引入 better-result。它指导开发者采用类型化错误处理并限定迁移范围,无需整体重写。
主要功能
- 01用 Result API 替换 try/catch
- 02将可空返回值改为 Result
- 03定义领域专用 TaggedError 类型
- 04使用 andThen 或 Result.gen 组合流程
- 05迁移时保留错误上下文
适用场景
- 01将 I/O 边界迁移到 Result
- 02替换模块中的 null 标记值
- 03统一服务中的错误类型
- 04重构嵌套错误处理
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dmmulroy/ better-result --skill better-result-adopt安装后在 Claude Code 中输入/better-result-adopt确认已加载。
附带文件references/tagged-errors.md
详细介绍
better-result 迁移
在现有代码库中逐步采用 better-result,无需一次性重写所有内容。
适用场景
当用户希望进行以下操作时,使用此技能:
- 将 try/catch 迁移为
Result.try或Result.tryPromise - 将可空返回值替换为类型化的
Result<T, E> - 定义特定领域的
TaggedError类型 - 将嵌套错误处理重构为
andThen链或Result.gen - 在服务或模块中统一错误处理方式
阅读顺序
| 任务 | 要阅读的文件 |
|---|---|
| 在模块中采用 better-result | 此文件 |
| 定义或审查错误类型 | references/tagged-errors.md |
| 检查库的实现细节 | 如存在,则检查 opensrc/ |
前置条件
编辑代码前:
- 确认目标项目已安装
better-result。 - 检查是否存在
opensrc/目录。如存在,阅读其中的包源代码,了解当前用法。 - 先确定迁移范围:一个文件、一个模块或一个边界层。
迁移策略
1. 从边界开始
从 I/O 边界和异常较多的代码入手:
- HTTP 客户端
- 数据库访问
- 文件系统操作
- 解析和验证
- 框架适配器
不要一次性转换整个代码库。
2. 对现有失败进行分类
| 类别 | 示例 | 目标形式 |
|---|---|---|
| 领域错误 | 未找到、验证、认证 | TaggedError + Result.err |
| 基础设施错误 | 网络、DB、文件 I/O | Result.tryPromise + 映射后的错误 |
| 程序员缺陷 | 错误假设、空值解引用 | 保留抛出行为;在 Result 回调中,缺陷会变成 Panic |
3. 按以下顺序迁移
- 定义错误类型。
- 使用
Result.try/Result.tryPromise包装会抛出异常的边界。 - 将 null 或布尔哨兵返回值替换为
Result。 - 重构调用点,以传递
Result值。 - 将嵌套分支收拢为
andThen、mapError或Result.gen。
核心转换
Try/catch → Result.try
function parseConfig(json: string): Result<Config, ParseError> {
return Result.try({
try: () => JSON.parse(json) as Config,
catch: (cause) => new ParseError({ cause, message: `Parse failed: ${cause}` }),
});
}
异步抛出 → Result.tryPromise
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: (cause) => (cause instanceof ApiError ? cause : new UnhandledException({ cause })),
});
}
Null 哨兵 → Result
function findUser(id: string): Result<User, NotFoundError> {
const user = users.find((candidate) => candidate.id === id);
return user
? Result.ok(user)
: Result.err(new NotFoundError({ id, message: `User ${id} not found` }));
}
嵌套流程 → Result.gen
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);
});
}
执行工作流
- 检查目标模块中的
try、catch、.catch(...)、throw、null、undefined和基于状态标记的错误处理。 - 在更改控制流之前,先定义或更新
TaggedError类。 - 优先转换边界函数,并将其签名改为
Result<T, E>或Promise<Result<T, E>>。 - 更新直接调用方,使其处理或传递新的
Result。 - 组合多个返回 Result 的步骤时,使用
Result.gen或andThen。 - 保留错误上下文,包括
cause、ID、消息和其他结构化字段。 - 运行测试,并为成功和错误路径添加覆盖。
完成标准
满足以下条件时,迁移即告完成:
- 目标函数不再依赖 try/catch 处理预期的领域失败
- 可空或哨兵错误返回值已替换为显式的
Result值 - 领域失败使用类型化的
TaggedError类 - 调用方会传递
Result,或显式对其进行解包或匹配 - 测试至少覆盖一个成功路径和一个有代表性的错误路径
常见陷阱
- 对所有内容都进行过度包装,而不是从边界开始
- 映射错误时丢失原始失败上下文
- 在同一流程的深处混用基于
throw和基于Result的 API - 捕获
Panic,而不是修复底层缺陷
本参考文档
| 文件 | 用途 |
|---|---|
references/tagged-errors.md | TaggedError 模式、匹配、类型守卫和示例 |
如果存在 opensrc/,请将其视为实现细节和当前 API 行为的准确信息来源。
评论
支持 Markdown