try / catch 错误处理与 catch 收窄
最后更新:2026-10-22
try / catch 错误处理与 catch 收窄
TS 错误处理有两个痛点:一是 catch 的 err 在严格模式下是 unknown(JS 可以 throw 任何值,编译器不敢假设它是 Error),必须收窄后才能用 .message;二是"会不会抛错"不体现在类型签名里,调用方无从知晓。对策:unknown 用 instanceof 收窄;对"可预期的失败"改用 Result 模式,把成败直接写进返回类型。
| 分组 | 工程与实战 |
|---|---|
| 类型 | 语法 |
| 严格模式 | 不需要 |
ℹ️ 说明 编辑器将 TypeScript 转译为 JavaScript 后运行(不做类型检查);类型检查结果见示例 Output。
📝 语法
// 1) unknown 收窄
try {
risky();
} catch (err: unknown) {
if (err instanceof Error) console.log(err.message);
else console.log("unknown throw:", err);
}
// 2) Result 模式:用联合类型表达"成功或失败"
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function attempt<T>(fn: () => T): Result<T> {
try { return { ok: true, value: fn() }; }
catch { return { ok: false, error: "failed" }; }
}
⚙️ 参数
| unknown | catch 变量的类型(严格模式下默认如此):什么都能装,但使用前必须收窄——这是安全性的来源 |
|---|---|
| instanceof | 最常用的收窄手段:err instanceof Error 后即可访问 .message/.stack;对自定义错误类同样有效 |
| Result<T> | 自实现的返回类型:{ ok: true; value: T } | { ok: false; error }。调用方必须先判断 ok,判断分支里各自只能拿到 value 或 error |
返回值:Result 模式的返回类型是判别联合:ok 为 true 才有 value,为 false 才有 error——"可能失败"被编码进类型,调用方不处理失败分支就无法编译通过。
▶ 示例
修改下面的代码,点击试一试查看转译运行结果:
输出:
caught RangeError: not a positive integer: abc
7 0
{"ok":true,"value":42}
failed: invalid input
(转译后运行输出;err 经 instanceof 收窄后才能访问 .message——不收窄直接写 err.message 会得到 error TS18046)
💡 要点
ℹ️ 说明 JS 允许 throw 任何值(数字、字符串、对象),所以 TS 把 catch 的 err 定为 unknown 而不是 Error——这是 useUnknownInCatchVariables(strict 包含它)带来的保护,防止对不存在的 .message 属性做空欢喜。
💡 提示 收窄手段不止 instanceof:typeof err === "string"、"message" in err、自定义类型守卫 isApiError(err) 都可以;写一个兜底分支处理"都不匹配"的情况,覆盖任意 throw 值。
⚠️ 注意 Result 模式会让调用方代码变长(每个调用点都要判断 ok)。常见折中:核心业务链路继续用异常,边界层(解析用户输入、调外部接口)转成 Result,把"可预期的失败"留给调用方显式处理。
🏷️ 相关工程与实战
💡 相关案例
更多案例即将上线。
📚 相关教程
❓ 常见问题
Q 为什么 catch 里不能直接用 err.message?
A 因为 err 的类型是 unknown(JS 可以 throw 任何值,包括字符串),编译器无法保证 .message 存在。先收窄:err instanceof Error、typeof err === "string" 或 "message" in err,之后才能安全访问。
Q 自定义错误类怎么收窄?
A extends Error 定义自己的类(如 ValidationError),catch 里用 err instanceof ValidationError 收窄,即可访问自定义字段(如 err.field)。同一次 try 里可以有多个收窄分支,从具体到一般排列。
Q 什么时候用异常,什么时候用 Result?
A 异常适合"意外情况"(bug、网络突然断了)——调用方多数时候不该处理;Result 适合"可预期失败"(解析输入、校验表单)——调用方必须处理,类型强迫它处理。判断标准:调用方拿到这个失败后,有没有有意义的下一步?