TypeScript错误处理:从基础到高级实践

发布时间:2026/9/10 9:24:14

TypeScript错误处理:从基础到高级实践 1. TypeScript错误处理基础概念在TypeScript开发中错误处理是保证代码健壮性的关键环节。与JavaScript类似TypeScript使用相同的错误处理机制但通过静态类型检查提供了更强的安全保障。我们先来看最基本的错误处理结构try { // 可能抛出错误的代码 throw new Error(Something went wrong); } catch (error) { // 错误处理逻辑 console.error(error.message); } finally { // 无论是否出错都会执行的代码 console.log(Cleanup operations); }这种结构看似简单但在TypeScript中有几个关键特性需要注意错误对象默认类型为any这不符合TypeScript的类型安全理念。我们可以通过类型注解来改进catch (error: unknown) { if (error instanceof Error) { console.error(error.message); } else { console.error(Unknown error occurred); } }TypeScript支持自定义错误类这在实际项目中非常有用class NetworkError extends Error { constructor(public statusCode: number, message: string) { super(message); this.name NetworkError; } } try { throw new NetworkError(404, API not found); } catch (error) { if (error instanceof NetworkError) { console.error(HTTP ${error.statusCode}: ${error.message}); } }2. TypeScript特有的错误处理模式2.1 类型保护与错误处理TypeScript的类型系统可以与错误处理完美结合。考虑以下场景function parseJSON(json: string): unknown { return JSON.parse(json); } const result parseJSON({valid: false}); if (result typeof result object valid in result) { // 类型安全地访问result.valid console.log(result.valid); }这种模式避免了直接使用any类型同时提供了类型安全的错误处理。2.2 异步错误处理的最佳实践在异步代码中错误处理尤为重要。以下是几种常见模式Promise的catch处理fetchData() .then(data processData(data)) .catch((error: Error) { console.error(Fetch failed:, error.message); });async/await模式async function loadUserData(userId: string) { try { const response await fetch(/users/${userId}); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } return await response.json(); } catch (error) { if (error instanceof Error) { console.error(Failed to load user:, error.message); } throw error; // 重新抛出以便上层处理 } }2.3 声明自定义错误类型对于大型项目定义一套完整的错误类型体系非常有用interface AppError { code: string; message: string; details?: unknown; } class ValidationError extends Error implements AppError { constructor(public code: string, message: string) { super(message); this.name ValidationError; } } function validateInput(input: string) { if (!input) { throw new ValidationError(INVALID_INPUT, Input cannot be empty); } }3. 高级错误处理技术3.1 错误边界与类型断言在React等前端框架中错误边界是处理组件错误的有效方式。在TypeScript中我们可以增强其类型安全性class ErrorBoundary extends React.Component{}, { hasError: boolean } { state { hasError: false }; static getDerivedStateFromError(error: unknown) { return { hasError: true }; } componentDidCatch(error: unknown, info: React.ErrorInfo) { logErrorToService(error, info.componentStack); } render() { if (this.state.hasError) { return FallbackUI /; } return this.props.children; } }3.2 函数式错误处理对于函数式编程风格我们可以使用Either模式type EitherL, R { left: L } | { right: R }; function safeParse(json: string): EitherError, unknown { try { return { right: JSON.parse(json) }; } catch (error) { return { left: error instanceof Error ? error : new Error(String(error)) }; } } const result safeParse(invalid json); if (left in result) { console.error(Parse failed:, result.left.message); } else { console.log(Parsed data:, result.right); }3.3 错误处理中间件在Express等后端框架中TypeScript可以增强错误中间件的类型安全interface ErrorRequestHandler P ParamsDictionary, ResBody any, ReqBody any, ReqQuery ParsedQs, Locals extends Recordstring, any Recordstring, any { ( err: any, req: RequestP, ResBody, ReqBody, ReqQuery, Locals, res: ResponseResBody, Locals, next: NextFunction ): void; } const errorHandler: ErrorRequestHandler (err, req, res, next) { if (err instanceof CustomError) { res.status(err.statusCode).json({ error: err.message, code: err.code }); } else { res.status(500).json({ error: Internal Server Error }); } };4. 实战中的错误处理技巧4.1 错误日志记录完善的错误日志是调试的关键。在TypeScript中我们可以创建类型安全的日志记录器interface LogEntry { timestamp: Date; level: error | warn | info; message: string; error?: Error; context?: Recordstring, unknown; } class Logger { log(entry: LogEntry) { console[entry.level]({ ...entry, timestamp: entry.timestamp.toISOString(), error: entry.error ? { name: entry.error.name, message: entry.error.message, stack: entry.error.stack } : undefined }); } }4.2 测试中的错误处理在单元测试中我们需要特别关注错误情况的测试describe(parseJSON, () { it(should throw for invalid JSON, () { expect(() parseJSON(invalid)).toThrow(SyntaxError); }); it(should return parsed data for valid JSON, () { const result parseJSON({valid:true}); expect(result).toEqual({ valid: true }); }); });4.3 性能考量错误处理对性能有一定影响特别是在热路径中避免在性能关键代码中使用try/catch对于可预测的错误使用返回码可能更高效错误对象的创建和堆栈跟踪收集是昂贵的操作// 不推荐在热路径中 function hotPath(input: string) { try { return process(input); } catch { return defaultValue; } } // 更高效的替代方案 function hotPath(input: string) { const result processIfValid(input); return result ! undefined ? result : defaultValue; }5. 常见错误处理模式对比5.1 抛出错误 vs 返回错误对象方法优点缺点适用场景抛出错误自动中断执行流强制处理性能开销大可能被忽略不可恢复的错误返回错误对象显式处理性能更好需要手动传播错误可恢复的错误5.2 不同错误处理库对比库名特点TypeScript支持适用场景neverthrow函数式风格Either模式优秀函数式项目ts-results简单的结果类型优秀小型项目fp-ts完整函数式工具集优秀大型函数式项目原生try/catch无需依赖一般简单场景5.3 错误处理策略选择指南前端UI使用错误边界和用户友好的错误提示API层返回结构化的错误响应服务层使用特定错误类型便于日志分析数据访问层将底层错误转换为领域错误6. TypeScript配置与错误处理6.1 tsconfig.json相关设置{ compilerOptions: { strict: true, noImplicitAny: true, strictNullChecks: true, useUnknownInCatchVariables: true } }这些设置可以增强错误处理的类型安全性useUnknownInCatchVariables将catch变量默认为unknown而非anystrictNullChecks防止null/undefined相关错误noImplicitAny避免隐式的any类型6.2 自定义类型守卫创建类型安全的错误判断函数function isErrorWithMessage(error: unknown): error is { message: string } { return typeof error object error ! null message in error typeof (error as any).message string; } try { // ... } catch (error) { if (isErrorWithMessage(error)) { console.error(error.message); } }6.3 声明合并增强Error通过声明合并为Error添加额外属性declare global { interface Error { code?: string; status?: number; } } const err new Error(Not found); err.code NOT_FOUND; err.status 404;7. 错误处理实用工具函数7.1 安全类型转换function safeCastT(value: unknown, guard: (v: unknown) v is T): T { if (guard(value)) { return value; } throw new TypeError(Value cannot be cast to target type); } // 使用示例 const isUser (v: unknown): v is User typeof v object v ! null id in v; try { const user safeCast(response.data, isUser); } catch (error) { console.error(Invalid user data); }7.2 重试机制async function withRetryT( operation: () PromiseT, maxRetries: number, delayMs: number ): PromiseT { let lastError: unknown; for (let i 0; i maxRetries; i) { try { return await operation(); } catch (error) { lastError error; if (i maxRetries - 1) { await new Promise(resolve setTimeout(resolve, delayMs)); } } } throw lastError instanceof Error ? lastError : new Error(String(lastError)); }7.3 错误聚合class AggregateError extends Error { constructor(public errors: Error[], message Multiple errors occurred) { super(message); this.name AggregateError; } } function parallelWithAggregateErrorT( tasks: (() PromiseT)[] ): PromiseT[] { return Promise.all(tasks.map(task task().catch(e e))) .then(results { const errors results.filter(isError); if (errors.length 0) { throw new AggregateError(errors); } return results as T[]; }); } function isError(value: unknown): value is Error { return value instanceof Error; }8. 错误处理与设计模式8.1 策略模式interface ErrorHandler { canHandle(error: unknown): boolean; handle(error: unknown): void; } class NetworkErrorHandler implements ErrorHandler { canHandle(error: unknown): boolean { return error instanceof Error error.message.includes(network); } handle(error: unknown) { if (!(error instanceof Error)) return; showToast(Network error, please check your connection); } } class ValidationErrorHandler implements ErrorHandler { canHandle(error: unknown): boolean { return error instanceof Error error.message.includes(validation); } handle(error: unknown) { if (!(error instanceof Error)) return; highlightInvalidFields(); } } const handlers: ErrorHandler[] [ new NetworkErrorHandler(), new ValidationErrorHandler() ]; function handleError(error: unknown) { const handler handlers.find(h h.canHandle(error)); if (handler) { handler.handle(error); } else { console.error(Unhandled error:, error); } }8.2 装饰器模式function withErrorHandling( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const originalMethod descriptor.value; descriptor.value async function(...args: any[]) { try { return await originalMethod.apply(this, args); } catch (error) { if (error instanceof Error) { console.error(Error in ${propertyKey}:, error.message); } throw error; } }; return descriptor; } class ApiService { withErrorHandling async fetchData(url: string) { const response await fetch(url); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } return response.json(); } }8.3 工厂模式interface ErrorFactory { create(error: unknown): Error; } class HttpErrorFactory implements ErrorFactory { create(error: unknown): Error { if (typeof error object error ! null) { if (status in error typeof error.status number) { return new HttpError(error.status, String(error.message || HTTP Error)); } } return new Error(String(error)); } } class HttpError extends Error { constructor(public status: number, message: string) { super(message); this.name HttpError; } } function createErrorHandler(factory: ErrorFactory) { return function handleError(error: unknown) { const normalizedError factory.create(error); console.error(${normalizedError.name}: ${normalizedError.message}); return normalizedError; }; }9. 错误处理与性能监控9.1 错误追踪集成interface ErrorTracker { captureException(error: unknown, context?: Recordstring, unknown): void; } class SentryErrorTracker implements ErrorTracker { constructor(private dsn: string) {} captureException(error: unknown, context?: Recordstring, unknown) { if (error instanceof Error) { // 实际项目中会调用Sentry SDK console.error(Sentry captured:, error.message, context); } else { console.error(Sentry captured unknown error:, error, context); } } } function withErrorTracking( tracker: ErrorTracker, fn: (...args: any[]) Promiseany ) { return async function(...args: any[]) { try { return await fn(...args); } catch (error) { tracker.captureException(error, { args }); throw error; } }; }9.2 性能指标收集interface ErrorMetrics { increment(errorType: string): void; timing(method: string, duration: number, success: boolean): void; } class PrometheusErrorMetrics implements ErrorMetrics { increment(errorType: string) { console.log(Metric incremented: errors_${errorType}_total); } timing(method: string, duration: number, success: boolean) { console.log(Method ${method} took ${duration}ms (success: ${success})); } } function instrumentWithMetrics( metrics: ErrorMetrics, fn: (...args: any[]) Promiseany ) { return async function(...args: any[]) { const start Date.now(); try { const result await fn(...args); metrics.timing(fn.name, Date.now() - start, true); return result; } catch (error) { metrics.timing(fn.name, Date.now() - start, false); if (error instanceof Error) { metrics.increment(error.constructor.name); } else { metrics.increment(UnknownError); } throw error; } }; }10. 错误处理最佳实践总结始终使用Error对象不要抛出原始字符串或其他类型区分错误类型创建特定的错误类来表示不同类型的错误保持类型安全利用TypeScript的类型系统来增强错误处理适当处理异步错误确保Promise和async/await中的错误被正确处理提供有意义的错误信息包含足够的上下文以便调试考虑错误恢复策略对于可恢复错误提供恢复路径记录错误确保所有错误都被适当记录以便后续分析监控生产环境错误使用错误跟踪系统监控生产环境中的错误测试错误场景编写测试验证错误处理逻辑文档化错误约定在团队中建立一致的错误处理规范在实际项目中错误处理策略应该根据项目规模、团队偏好和具体需求进行调整。TypeScript提供的类型系统可以大大增强错误处理的安全性和可维护性关键是找到适合你项目的平衡点。
延伸阅读

更多相关文章

2026/9/9 6:23:50

Redis 3.x——配置统计字典

配置统计字典1、info系统状态说明1.1、命令说明1.2、详细说明1.2.1、Server1.2.2、Clients1.2.3、Memory1.2.4、Persistence1.2.5、Stats1.2.6、Replication1.2.7、CPU1.2.8、Commandstats1.2.9、Cluster1.2.10、Keyspace2、standalone配置说明和分析2.1、总体配置2.2、最大内存…

2026/9/9 4:55:13

STM32定时器PWM电子琴仿真:Proteus与Keil实战教程

这次我们来看一个基于STM32单片机的多音电子琴Proteus仿真项目。这个设计以STM32F103系列单片机为核心,通过七个琴键控制蜂鸣器演奏不同音区的音符(低音、中音、高音),使用LED指示灯实时反馈按键状态,并通过数码管显示…

2026/9/10 9:17:02

AI玩具机芯打样全流程:从需求冻结到量产落地

1. 这不是玩具,是精密机电系统的微型战场“AI 玩具机芯打样流程:从需求冻结到量产”——这行字乍看像儿童产品开发简报,实则是一场横跨机械、电子、嵌入式软件、人机交互与小批量制造的多线程攻防战。我做智能硬件打样十年,经手过…

2026/9/9 13:11:35

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/9 10:21:54

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码