Express.js与TypeScript:如何简化错误处理中间件app.use回调的参数类型
问题根因
Express 的 TypeScript 类型定义通过函数重载区分两类不同的中间件:
- 普通业务中间件签名为
(req, res, next) => void,这类中间件不需要接收错误对象作为第一个参数,你写的404捕获逻辑正好匹配这个重载,所以TS能自动为三个参数推导出正确类型,不会报错 - 错误处理专属中间件签名为
(err, req, res, next) => void——这个重载的匹配优先级低于普通中间件签名,当你直接传一个四参数的匿名回调、没有做类型标注的时候,TS无法判断你写的是哪类中间件,类型推导直接失效,最终所有参数都退化成隐式any,触发TS7006报错。
高可维护性实现方案
优先方案:复用Express官方提供的错误中间件类型
这是最简洁、无冗余、可维护性最高的实现方式,不需要手动给每个参数堆一长串类型标注,只要告诉TS当前写的是错误处理中间件,所有参数类型都会自动推导完成:
- 确保项目已经安装
@types/express依赖(所有TS版Express项目默认都会安装) - 从express类型包里引入专门的错误处理中间件类型
ErrorRequestHandler
import type { ErrorRequestHandler } from 'express'; import createError from 'http-errors'; // 404捕获逻辑保持原样,无需任何修改 app.use((req, res, next) => { next(createError(404)); }); // 错误处理器:只要给函数标注ErrorRequestHandler类型,所有参数自动获得正确类型 const errorHandler: ErrorRequestHandler = (err, req, res, next) => { res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; res.status(err.status || 500); res.render('error'); }; // 注册错误中间件 app.use(errorHandler);
这个写法的优势:
- 全程没有手写冗余类型,代码可读性和原生JS版本几乎一致
- 所有参数类型完全由Express官方维护,不会因为版本迭代出现类型过时的问题
- 不存在隐式
any,完全符合ESLint规则要求 - TS能正常帮你做类型校验:比如你写错了
res.render的参数、误读了err上不存在的属性,都会第一时间报错
简化内联写法
如果你不想把错误处理器单独抽成变量,也可以直接在注册中间件的位置通过类型断言指定回调类型,代码结构和原来的JS版本几乎完全一致:
import type { ErrorRequestHandler } from 'express'; import createError from 'http-errors'; app.use((req, res, next) => next(createError(404))); app.use(((err, req, res, next) => { res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; res.status(err.status || 500); res.render('error'); }) as ErrorRequestHandler);
避坑提醒
- 不要为了消掉报错把所有参数手动标成
any:一方面会触发ESLint的隐式any规则告警,另一方面会完全丢失TS的类型检查保护,后续代码写错属性、传错参数都不会有提示 - 不要使用VSCode自动生成的冗长类型标注:自动补全生成的类型会把每个参数的完整泛型定义全部展开,单个参数的类型就可能占大半个屏幕,后续维护成本极高
内容的提问来源于stack exchange,提问作者Hauke P.
相关产品推荐
相关产品推荐

