引言:为什么 TypeScript 中的错误处理不仅仅是 try/catch
TypeScript 为 JavaScript 增加了静态类型检查,但运行时错误(runtime errors)仍然是任何应用程序的一部分。与纯 JS 不同,在纯 JS 中 catch(e) 返回 any,而 TypeScript 提供了更严格的机制。正确的错误处理不仅是为了防止崩溃,更是为了创建一个易于调试和维护代码的清晰系统。在本文中,我们将分析所有方面:从基本的 try/catch 到创建具有完整类型化的自定义错误层次结构。
1. try/catch 的基本语法和 unknown 类型的问题
经典结构
TypeScript 中的基本结构与 JavaScript 相同:
try { // 可能抛出异常的代码 const data = JSON.parse('{"name": "TypeScript"}'); console.log(data.name);} catch (error) { // 错误处理 console.error('发生错误:', error);}但有一个重要区别:在 TypeScript 中,catch 块中的变量 error 的类型是 unknown(而不是 JavaScript 中未指定时的 any)。这是为了类型安全。
unknown 的问题及其解决方法
unknown 类型意味着我们不能直接访问错误的属性,例如 error.message。编译器会报错:
catch (error) { // 错误 TS2571: Object is of type 'unknown'. console.log(error.message); // ❌ 无法编译}要访问属性,需要收窄类型:
catch (error) { if (error instanceof Error) { console.log(error.message); // ✅ 正常工作 } else { console.log('未知错误:', error); }}或者使用属性存在性检查:
catch (error) { if (error && typeof error === 'object' && 'message' in error) { console.log((error as { message: string }).message); }}2. 创建带类型化的自定义错误类
为什么这很重要?
标准的 Error 只包含 name、message 和 stack。在实际项目中,通常需要额外的字段:错误代码、HTTP 状态码、用于日志记录的数据。TypeScript 允许创建具有完整类型支持的错误类层次结构。
自定义类示例
// 所有应用程序错误的基础类class AppError extends Error { public readonly statusCode: number; public readonly isOperational: boolean;
constructor(message: string, statusCode: number = 500, isOperational: boolean = true) { super(message); this.name = 'AppError'; this.statusCode = statusCode; this.isOperational = isOperational; // 恢复原型(TypeScript 需要) Object.setPrototypeOf(this, new.target.prototype); }}
// 专门化的错误class NotFoundError extends AppError { constructor(resource: string) { super(`${resource} 未找到`, 404); this.name = 'NotFoundError'; }}
class ValidationError extends AppError { public readonly errors: string[];
constructor(errors: string[]) { super('验证错误', 400); this.name = 'ValidationError'; this.errors = errors; }}与 try/catch 一起使用
function getUser(id: number): User { if (id <= 0) { throw new ValidationError(['ID 必须为正数']); } // ... 获取用户的逻辑 if (!user) { throw new NotFoundError('用户'); } return user;}
try { const user = getUser(-1);} catch (error) { if (err
```