TypeScript实现支持类型推导的Express中间件管道函数
实现支持任意数量中间件的Express Request属性增强工具
你需要构建一个工具函数,让Express中间件可以逐步为Request对象添加属性,且后续中间件能在保留类型提示的前提下访问这些属性。当前的实现仅支持固定数量的中间件,需要手动扩展类型参数,不够灵活。以下是优化后的解决方案,支持任意数量的中间件参数,并保持完整的类型推导。
需求示例
期望的使用方式如下,后续中间件能直接访问前面步骤添加的属性:
app.post( '/some/endpoint', pipeMiddleware( (req, res) => { // 执行异步操作 return { entity: { /* ... */ } }; }, (req, res) => { // 此处可直接访问req.entity return { otherEntity: { /* ... */ } }; }, (req, res) => { // 此处可同时访问req.entity和req.otherEntity handler(req.entity, req.otherEntity, req.body, res); return {}; } ) );
优化后的实现
通过递归类型推导和动态中间件执行,实现支持任意数量步骤的工具函数:
import { Request, Response, NextFunction } from 'express'; // 定义单个中间件步骤的类型:接收当前Request,返回要添加的属性对象 type MiddlewareStep<Req extends Request, AddedProps> = ( req: Req, res: Response ) => Promise<AddedProps>; // 递归类型,用于累积中间件链的Request属性类型 type MiddlewareChain<Req extends Request, AccumulatedProps = {}> = { // 终止调用:生成最终的Express中间件 (): (req: Req, res: Response, next?: NextFunction) => Promise<Req & AccumulatedProps>; // 添加一个中间件步骤,累积属性类型 <NextProps>( step: MiddlewareStep<Req & AccumulatedProps, NextProps> ): MiddlewareChain<Req, AccumulatedProps & NextProps>; }; function pipeMiddleware<Req extends Request = Request>( ...steps: MiddlewareStep<any, any>[] ): (req: Req, res: Response, next?: NextFunction) => Promise<Req & Record<string, any>>; function pipeMiddleware<Req extends Request = Request>(): MiddlewareChain<Req>; function pipeMiddleware<Req extends Request = Request>( ...args: any[] ): any { const steps: MiddlewareStep<any, any>[] = []; // 链式调用模式:初始无参数,逐步添加中间件 if (args.length === 0) { const chain: any = (step?: MiddlewareStep<any, any>) => { if (!step) { // 执行所有中间件步骤 return async (req: Req, res: Response, next?: NextFunction) => { let currentReq = req as Req & Record<string, any>; try { for (const stepFn of steps) { const addedProps = await stepFn(currentReq, res); Object.assign(currentReq, addedProps); } next?.(); return currentReq; } catch (err) { next?.(err); throw err; } }; } steps.push(step); return chain; }; return chain; } // 直接传入多个中间件的模式 steps.push(...args); return async (req: Req, res: Response, next?: NextFunction) => { let currentReq = req as Req & Record<string, any>; try { for (const stepFn of steps) { const addedProps = await stepFn(currentReq, res); Object.assign(currentReq, addedProps); } next?.(); return currentReq; } catch (err) { next?.(err); throw err; } }; }
核心特性
- 任意数量中间件:无需手动扩展类型参数,支持传入任意多个中间件步骤
- 完整类型推导:每个中间件的
req参数会自动包含前面所有步骤添加的属性,TypeScript能正确提供类型提示和检查 - 两种调用方式:支持直接传入多个中间件,或通过链式调用逐步添加
- 错误处理兼容:兼容Express的
NextFunction,错误会传递给后续错误处理中间件
使用示例
直接传入多个中间件
app.post( '/some/endpoint', pipeMiddleware( async (req, res) => { const entity = await fetchEntity(req.params.id); return { entity }; }, async (req, res) => { // 此处req.entity类型已被正确推导 const otherEntity = await fetchRelatedEntity(req.entity.id); return { otherEntity }; }, async (req, res) => { // 此处可访问req.entity和req.otherEntity await saveData(req.entity, req.otherEntity); res.status(200).json({ success: true }); return {}; } ) );
链式调用添加中间件
app.get( '/user/profile', pipeMiddleware() .addStep(async (req, res) => { const user = await getUser(req.headers.authorization); return { user }; }) .addStep(async (req, res) => { const profile = await getUserProfile(req.user.id); return { profile }; }) .addStep(async (req, res) => { res.status(200).json({ user: req.user, profile: req.profile }); return {}; })() // 最后调用()生成Express中间件 );
内容的提问来源于stack exchange,提问作者mikey-mike
相关产品推荐
相关产品推荐

