better-result-adopt by dmmulroy/better-result
npx skills add https://github.com/dmmulroy/better-result --skill better-result-adopt将现有的错误处理(try/catch、Promise 拒绝、抛出异常)迁移到基于类型化 Result 的错误处理,使用 better-result。
从 I/O 边界(API 调用、数据库查询、文件操作)开始迁移,然后向内推进。不要试图一次性迁移整个代码库。
在迁移之前,对目标代码中的错误进行分类:
| 类别 | 示例 | 迁移目标 |
|---|---|---|
| 领域错误 | NotFound、Validation | TaggedError + Result.err |
| 基础设施错误 | Network、DB connection | Result.tryPromise + TaggedError |
| 缺陷/错误 | null 解引用、类型错误 | 让其抛出(如果在 Result 回调中则变为 Panic) |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
// 迁移前
function parseConfig(json: string): Config {
try {
return JSON.parse(json);
} catch (e) {
throw new ParseError(e);
}
}
// 迁移后
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 function fetchUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new ApiError(res.status);
return res.json();
}
// 迁移后
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 })),
});
}
// 迁移前
function findUser(id: string): User | null {
return users.find((u) => u.id === id) ?? null;
}
// 调用者必须检查:if (user === null) ...
// 迁移后
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()
// 迁移前
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;
}
}
// 迁移后
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 错误的联合类型
有关 TaggedError 模式,请参阅 references/tagged-errors.md。
opensrc/ 目录 - 如果存在,请阅读 better-result 源代码以了解实现细节和模式opensrc/ 目录(如果存在) - 完整的 better-result 源代码,用于更深入的上下文每周安装量
129
代码仓库
GitHub 星标数
983
首次出现
2026年1月19日
安全审计
安装于
opencode106
codex96
gemini-cli87
github-copilot85
claude-code74
amp74
Migrate existing error handling (try/catch, Promise rejections, thrown exceptions) to typed Result-based error handling with better-result.
Begin migration at I/O boundaries (API calls, DB queries, file ops) and work inward. Don't attempt full-codebase migration at once.
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) |
// 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}` }),
});
}
// 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 })),
});
}
// 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()
// 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 errors
See references/tagged-errors.md for TaggedError patterns.
opensrc/ directory - if present, read the better-result source code for implementation details and patternsopensrc/ directory (if present) - Full better-result source code for deeper contextWeekly Installs
129
Repository
GitHub Stars
983
First Seen
Jan 19, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
opencode106
codex96
gemini-cli87
github-copilot85
claude-code74
amp74
Tailwind CSS v4 + shadcn/ui 生产级技术栈配置指南与最佳实践
2,600 周安装