用途
better-result-adopt 用于在已存在的 TypeScript 代码库中,以增量方式引入 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/ 目录,若存在则阅读其中包源码以了解当前模式;先确定迁移范围,是一个文件、一个模块还是一个边界层。
迁移策略
-
从边界开始。优先处理 I/O 边界和异常密集代码,包括 HTTP 客户端、数据库访问、文件系统操作、解析与校验、框架适配器。不要一次性转换整个代码库。
-
对现有失败进行分类:
- 领域错误(not found、validation、auth)目标形态为 TaggedError + Result.err;
- 基础设施错误(network、DB、file I/O)目标形态为 Result.tryPromise + 映射后的错误;
- 程序员缺陷(错误假设、空指针解引用)保持抛出;缺陷会成为 Result 回调内部的 Panic。
-
按以下顺序迁移:定义错误类型;用 Result.try / Result.tryPromise 包裹会抛出的边界;用 Result 替换 null 或布尔哨兵返回;重构调用点以传播 Result 值;把嵌套分支折叠为 andThen、mapError 或 Result.gen。
核心转换
文档给出四类转换示例:
- try/catch → Result.try:在 try 回调中执行 JSON.parse,在 catch 回调中返回带 cause 和 message 的 ParseError,函数返回类型为 Result<Config, ParseError>。
- 异步抛错 → Result.tryPromise:在 try 中执行 fetch 并在 !res.ok 时抛出 ApiError,catch 中若 cause 非 ApiError 则包装为 UnhandledException,返回 Promise<Result<User, ApiError | UnhandledException>>。
- null 哨兵 → Result:查找用户时命中返回 Result.ok(user),未命中返回 Result.err(new NotFoundError(...)),返回类型为 Result<User, NotFoundError>。
- 嵌套流程 → Result.gen:使用 Result.gen(async function* () {...}) 配合 yield* Result.await(...) 依次获取订单、校验订单、提交订单,最终返回 Result.ok(result)。
执行工作流
审计目标模块中的 try、catch、.catch(...)、throw、null、undefined 以及状态标志式错误处理;在改动控制流之前先定义或更新 TaggedError 类;先转换边界函数并把签名改为 Result<T, E> 或 Promise<Result<T, E>>;更新直接调用方,使其处理或传播新的 Result;多个返回 Result 的步骤组合时使用 Result.gen 或 andThen;通过保留 cause、ID、message 及其他结构化字段来维持错误上下文;运行测试并为成功与错误路径补充覆盖率。
完成标准
目标函数不再依赖 try/catch 处理可预期的领域失败;可空或哨兵式的错误返回被替换为显式的 Result 值;领域失败使用类型化的 TaggedError 类;调用方要么传播 Result,要么显式解包或匹配;测试至少覆盖一条成功路径和一条代表性错误路径。
常见陷阱
- 什么都包裹,而不是从边界开始;
- 映射错误时丢失原始失败上下文;
- 在同一条流程深处混用基于 throw 和基于 Result 的 API;
- 捕获 Panic 而不是修复底层缺陷。
参考文档
references/tagged-errors.md:说明 TaggedError 的模式、匹配、类型守卫与示例。若存在 opensrc/,应将其视为实现细节与当前 API 行为的事实来源。