You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题原因

  1. 泛型参数P extends Function[]过于宽松,Function类型几乎不做任何类型校验,导致TypeScript无法检查每个参数生成函数的返回值是否匹配handler的参数类型。
  2. 默认值[] as P的写法会让TypeScript对空数组的类型推断失效,进一步弱化类型校验。
  3. 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]);

修复说明

  1. 重新定义泛型参数P:直接绑定到Parameters<H>的每个元素,要求每个数组元素必须是返回对应类型的(req: Request) => T函数,让TypeScript能严格校验类型匹配。
  2. 给morph和applyTransformers添加泛型类型,避免使用any和Function这类宽泛类型,增强类型安全性。
  3. 用for...of替代for...in遍历转换器数组,解决原代码的逻辑错误。
  4. 用Awaited<ReturnType<H>>获取handler返回Promise的实际类型,让转换器的类型约束更准确。

内容的提问来源于stack exchange,提问作者ibodi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 12:53:12