TypeScript中为route函数泛型实现handler与params类型匹配的问题
Express路由函数类型自动匹配修复
我在给Express项目的route函数做类型定义,希望TypeScript能自动校验handler和params的类型是否匹配——当二者类型不匹配时抛出TypeError,但目前的实现做不到。比如handler类型是(string, number) => Promise<any>,params传[(req: Request) => number, (req: Request) => string]时,TypeScript完全不报错,而且我不想手动指定泛型参数。
原代码
import { Request, Response, RequestHandler, ErrorRequestHandler } from 'express'; // 假设wrap是已有的错误处理包裹函数 const wrap = (handler: RequestHandler) => handler; const morph = (params: Function[]) => (req: Request) => params.map(f => f(req)) const applyTransformers = (transformers: Function[]) => (response: any) => { for (const transformer in transformers) { response = transformer(response) } return response } export const route = < H extends (...args: any[]) => Promise<any>, T extends any[] = Parameters<H>, P extends Function[] = { [K in keyof T]: (req?: Request) => T[K] } >(handler: H, params: P = [] as P, transformers: Function[] = []): RequestHandler | ErrorRequestHandler => ( wrap( async (req: Request, res: Response) => { const result = await handler( ...morph(params)(req) ) res.send(applyTransformers(transformers)(result)) } ) ) // 使用示例 // const id = (req: Request): string => req.params.id // const customParam = (param: string) => (req: Request): string => req.params[param] // app.get('/example-route/:id/blah/:blah', route(handler, [id, customParam('blah')])) // handler: (string, string) => Promise<any>
问题原因
- 泛型参数
P extends Function[]过于宽松,Function类型几乎不做任何类型校验,导致TypeScript无法检查每个参数生成函数的返回值是否匹配handler的参数类型。 - 默认值
[] as P的写法会让TypeScript对空数组的类型推断失效,进一步弱化类型校验。 applyTransformers里用for...in遍历数组,拿到的是索引字符串而非数组元素,实际运行时会触发调用错误。
修复后的代码
import { Request, Response, RequestHandler, ErrorRequestHandler } from 'express'; const wrap = (handler: RequestHandler) => handler; // 给morph添加严格泛型类型 const morph = <T>(params: readonly ((req: Request) => T)[]) => (req: Request): T[] => params.map(f => f(req)); // 给transformers添加类型约束,修正遍历逻辑 const applyTransformers = <T>(transformers: readonly ((res: T) => T)[]) => (response: T): T => { for (const transformer of transformers) { response = transformer(response); } return response; }; export const route = < H extends (...args: any[]) => Promise<any>, // 直接绑定P到handler参数的生成函数数组,无需宽松约束 P extends readonly { [K in keyof Parameters<H>]: (req: Request) => Parameters<H>[K] } >( handler: H, // 对齐空数组默认值的类型 params: P = [] as unknown as P, // 约束转换器与handler返回值类型一致 transformers: readonly ((res: Awaited<ReturnType<H>>) => Awaited<ReturnType<H>>)[] = [] ): RequestHandler | ErrorRequestHandler => wrap(async (req: Request, res: Response) => { const args = morph(params)(req); const result = await handler(...args as Parameters<H>); const transformedResult = applyTransformers(transformers)(result); res.send(transformedResult); }); // 测试示例 const handler = (a: string, b: number): Promise<string> => Promise.resolve(`${a}-${b}`); const getStringParam = (req: Request): string => req.params.id; const getNumberParam = (req: Request): number => parseInt(req.params.num, 10); const wrongParam = (req: Request): string => req.params.num; // ✅ 类型匹配,无报错 route(handler, [getStringParam, getNumberParam]); // ❌ 类型不匹配,TypeScript会抛出错误 route(handler, [getStringParam, wrongParam]);
修复说明
- 重新定义泛型参数
P:直接绑定到Parameters<H>的每个元素,要求每个数组元素必须是返回对应类型的(req: Request) => T函数,让TypeScript能严格校验类型匹配。 - 给
morph和applyTransformers添加泛型类型,避免使用any和Function这类宽泛类型,增强类型安全性。 - 用
for...of替代for...in遍历转换器数组,解决原代码的逻辑错误。 - 用
Awaited<ReturnType<H>>获取handler返回Promise的实际类型,让转换器的类型约束更准确。
内容的提问来源于stack exchange,提问作者ibodi
相关产品推荐
相关产品推荐

