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

TypeScript如何定义函数数组实现CTX类型依次扩展前序返回值

TypeScript 累加上下文类型的中间件数组实现方案

核心通过递归类型推导,自动约束每一步中间件的入参上下文,同时累加所有中间件的返回值到最终上下文类型中。

第一步:定义基础工具类型

// 基础中间件类型约束:接收当前上下文,返回需要合并到上下文的新属性
type Middleware<CurrentCtx, Return extends Record<string, any>> = (ctx: CurrentCtx) => Return;

// 递归约束中间件数组:每一项的入参自动包含之前所有中间件的返回值
type MiddlewareArray<BaseCtx, Middlewares extends any[], AccuReturn = {}> = 
  Middlewares extends [infer FirstMiddleware, ...infer RestMiddlewares]
    ? FirstMiddleware extends Middleware<BaseCtx & AccuReturn, infer CurrReturn>
      ? [FirstMiddleware, ...MiddlewareArray<BaseCtx, RestMiddlewares, AccuReturn & CurrReturn>]
      : never
    : [];

// 提取中间件数组所有返回值的合并类型,用于生成最终上下文
type MergeMiddlewareReturns<BaseCtx, Middlewares extends any[]> = 
  Middlewares extends [infer FirstMiddleware, ...infer RestMiddlewares]
    ? FirstMiddleware extends Middleware<BaseCtx & infer _, infer CurrReturn>
      ? CurrReturn & MergeMiddlewareReturns<BaseCtx, RestMiddlewares>
      : never
    : {};

第二步:封装符合场景的Api实例类型

// 自定义基础请求上下文,可根据业务扩展
type BaseReqCtx = {
  req: Request;
  res: Response;
};

// Api实例类型,泛型存储全局累加的上下文类型
type ApiInstance<GlobalCtx extends {} = {}> = {
  // use方法注册全局中间件,返回新的实例,全局上下文自动累加
  use: <Middlewares extends any[]>(middlewares: MiddlewareArray<BaseReqCtx, Middlewares>) => 
    ApiInstance<GlobalCtx & MergeMiddlewareReturns<BaseReqCtx, Middlewares>>;
  // get方法注册路由中间件和处理函数,最终处理函数的ctx自动包含所有中间件返回值
  get: <Middlewares extends any[]>(
    middlewares: MiddlewareArray<BaseReqCtx & GlobalCtx, Middlewares>,
    handler: (req: Request, res: Response, ctx: BaseReqCtx & GlobalCtx & MergeMiddlewareReturns<BaseReqCtx & GlobalCtx, Middlewares>) => void
  ) => ApiInstance<GlobalCtx>;
};

// 声明api工厂函数
declare function api(): ApiInstance;

验证示例

// 示例业务类型,需根据实际业务自行定义
interface IORedis {}
interface IUser {}
interface IGuild {}

// 示例中间件定义
declare const redis: (ctx: BaseReqCtx) => { redis: IORedis };
declare const loggedIn: (ctx: BaseReqCtx & { redis: IORedis }) => { token: string, session: string };
declare const getUser: (ctx: BaseReqCtx & { redis: IORedis, token: string, session: string }) => { user: IUser };
declare const getGuild: (ctx: BaseReqCtx & { redis: IORedis, token: string, session: string, user: IUser }) => { guild: IGuild };

// 实际调用测试
export default api()
  .use([redis, loggedIn])
  .get([getUser, getGuild], (req,res,ctx) => {
    // 此时ctx会自动推导出所有属性,无需手动声明类型
    console.log(ctx.redis)
    console.log(ctx.token)
    console.log(ctx.session)
    console.log(ctx.user)
    console.log(ctx.guild)
})

补充说明

  • 中间件返回值需要是对象类型,才能和上下文做交叉合并,不需要返回额外属性时返回{}即可。
  • 其他请求方法如post、put等可以参考get方法的类型定义扩展。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 20:09:03