TypeScript如何从参数推断Express回调的泛型类型
解决方案
核心原因
你当前的泛型参数都设置了默认值,TypeScript 推断类型时会优先填充默认值,不会等待从 app.get 的路由路径上下文获取具体的参数类型,因此 req.params 会被推断为通用的 ParamsDictionary(带任意字符串索引签名),自然不会有参数名的类型校验。
最优修复方案
移除首个泛型参数 P 的默认值,强制 TypeScript 从外层上下文推断类型,不需要额外修改调用逻辑:
import { RequestHandler, ParamsDictionary, ParsedQs } from "express"; function tryCatchNext< // 移除P的默认值,强制从上下文推断 P extends ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery extends ParsedQs = ParsedQs, Locals extends Record<string, any> = Record<string, any> >( callback: RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> ): RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> { return async (req, res, next) => { try { await callback(req, res, next); } catch (err) { next(err); } }; }
修改后直接按你原来的方式调用即可,req.params 会自动从路由路径中推断出存在的字段:
app.get('/:foo', tryCatchNext(async (req, res, next) => { // 正常识别foo参数 if (req.params.foo === 'bar') { throw new Error('That was a bar.'); } // 此处会触发类型错误,符合预期 console.log(req.params.bar); res.send('Not a bar'); }));
兼容无上下文场景的方案
如果你需要在没有外层上下文的地方使用 tryCatchNext,可以用函数重载同时支持两种场景:
import { RequestHandler, ParamsDictionary, ParsedQs } from "express"; // 重载1:有上下文可推断的场景,不需要传泛型 function tryCatchNext< P extends ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery extends ParsedQs = ParsedQs, Locals extends Record<string, any> = Record<string, any> >( callback: RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> ): RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals>; // 重载2:无上下文的场景,使用默认泛型 function tryCatchNext( callback: RequestHandler ): RequestHandler; // 实现 function tryCatchNext( callback: RequestHandler ): RequestHandler { return async (req, res, next) => { try { await callback(req, res, next); } catch (err) { next(err); } }; }
轻量替代方案
如果不想修改工具函数定义,也可以在调用时直接给回调的 req 参数标注类型:
import { Request } from "express"; app.get('/:foo', tryCatchNext(async (req: Request<{foo: string}>, res, next) => { // 同样可以获得正确的类型校验 }));
内容的提问来源于stack exchange,提问作者dx_over_dt
相关产品推荐
相关产品推荐

