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

如何让自定义异步Express Handler支持泛型类型参数传递

问题描述

我需要定义一个自定义Express端点包装器,用来包装异步函数并处理错误(未来可扩展更多功能),已经实现了基础的Handler,但使用它包装路由处理函数后,Express路由方法的泛型类型校验失效了。

基础Handler代码:

import type {Request, RequestHandler, Response} from 'express';

type Handler = (
    fn: (req: Request, res: Response) => Promise<void> | void
) => RequestHandler;

const handler: Handler = fn => async (req, res, next) => {
    try {
        await fn(req, res);
    } catch (e) {
        next(e);
    }
};

直接使用Express路由泛型时类型校验有效:

import express from 'express';
const app = express();

app.get<{}, {data: number}>(
    '/posts',
    (_req, res) => {
        // @ts-expect-error Argument of type '{ hello: string; }' is not assignable to parameter of type '{ data: number; }'.
        res.send({hello: 'world'});
    }),
);

但用自定义Handler包装后,类型校验失效,编译器不会报错:

import express from 'express';
import type {Request, RequestHandler, Response} from 'express';

type Handler = (
    fn: (req: Request, res: Response) => Promise<void> | void
) => RequestHandler;

const handler: Handler = fn => async (req, res, next) => {
    try {
        await fn(req, res);
    } catch (e) {
        next(e);
    }
};

const app = express();
app.get<{}, {data: number}>(
    '/posts',
    handler((_req, res) => {
        // @ts-expect-error 此处应触发类型错误但实际未生效
        res.send({should_not: 'compile'});
    })
);

app.listen(4000, () => console.log('Listening on port 4000...'));
解决方案

问题核心是原Handler类型没有保留Express路由的泛型参数,需要将Handler定义为泛型函数,让它能接收并传递Express的请求、响应等泛型类型。

修改后的完整代码:

import express, { Request, Response, RequestHandler, ParamsDictionary, Query } from 'express';

// 让 Handler 对齐 Express RequestHandler 的泛型参数定义
type Handler = <
    P = ParamsDictionary,
    ResBody = any,
    ReqBody = any,
    ReqQuery = Query,
    Locals extends Record<string, any> = Record<string, any>
>(
    fn: (req: Request<P, ResBody, ReqBody, ReqQuery, Locals>, res: Response<ResBody, Locals>) => Promise<void> | void
) => RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals>;

const handler: Handler = fn => async (req, res, next) => {
    try {
        await fn(req, res);
    } catch (e) {
        next(e);
    }
};

const app = express();
app.get<{}, {data: number}>(
    '/posts',
    handler((_req, res) => {
        // 现在会正确触发类型错误:类型"{ should_not: string; }"不能赋值给类型"{ data: number; }"
        res.send({should_not: 'compile'});
    })
);

app.listen(4000, () => console.log('Listening on port 4000...'));

关键修改说明

  • 将Handler从普通类型改为泛型类型,泛型参数完全匹配Express的RequestHandler、Request和Response的泛型定义:
    • P:路由参数类型(默认ParamsDictionary)
    • ResBody:响应体类型(默认any)
    • ReqBody:请求体类型(默认any)
    • ReqQuery:请求查询参数类型(默认Query)
    • Locals:响应本地变量类型(默认空对象)
  • 包装函数的入参fn和返回的RequestHandler都使用同一套泛型参数,确保Express路由的泛型信息能完整传递到包装后的处理函数中,恢复类型校验能力。

内容的提问来源于stack exchange,提问作者The bullz 123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 16:01:01