TypeScript中如何为Express.Request添加多类型自定义属性?
解决Express Request自定义属性多类型冲突问题
你碰到的问题本质是重复全局扩展Request接口导致的类型合并冲突——TypeScript会把多个文件里对同一个接口的属性定义合并,所以store的类型会变成两个函数类型的交集(而交集在这里是无效的,因为参数和返回值都不兼容),从而报错。
下面提供两种不需要条件类型的解决方案:
方案一:全局联合类型+配套Request子类型
步骤1:全局定义联合类型的store
在项目的类型声明文件(比如types/express.d.ts)里统一扩展Request,把store定义为可选的联合类型:
// types/express.d.ts declare module 'express-serve-static-core' { interface Request { // 定义store为两种可能的函数类型的联合 store?: ((name: string) => number) | ((key: number) => string); } } // 确保这个文件被TypeScript识别 export {};
步骤2:在中间件文件里明确赋值并导出配套Request类型
在a.ts里,给store赋值对应类型的函数,同时导出一个标记了当前store类型的Request子类型:
// a.ts import { Request, Response, NextFunction } from 'express'; // 假设setInStore是你已实现的函数 const setInStore = (name: string): number => { return parseInt(name, 10) || 0; }; const func1 = (req: Request, res: Response, next: NextFunction) => { // 直接赋值对应类型的函数,TypeScript会自动校验 req.store = (name: string): number => { return setInStore(name); }; next(); }; // 导出配套的Request类型,供使用者明确类型 export type RequestWithSetStore = Request & { store: (name: string) => number; }; export default func1;
b.ts做类似处理:
// b.ts import { Request, Response, NextFunction } from 'express'; // 假设getFromStore是你已实现的函数 const getFromStore = (key: number): string => { return `value_${key}`; }; const func2 = (req: Request, res: Response, next: NextFunction) => { req.store = (key: number): string => { return getFromStore(key); }; next(); }; // 导出配套的Request类型 export type RequestWithGetStore = Request & { store: (key: number) => string; }; export default func2;
步骤3:在使用时导入对应Request类型
在c.ts里,使用中间件后,在路由处理函数里明确导入对应的Request子类型,就能获得正确的类型提示:
// c.ts import express from 'express'; import func1, { RequestWithSetStore } from './a'; import func2, { RequestWithGetStore } from './b'; const app = express(); // 使用func1的路由组 app.use(func1); app.get('/set', (req: RequestWithSetStore, res) => { // 这里store的类型是明确的 (name: string) => number const result = req.store('123'); res.send(`Set result: ${result}`); }); // 使用func2的独立路由组 const getRouter = express.Router(); getRouter.use(func2); getRouter.get('/get', (req: RequestWithGetStore, res) => { // 这里store的类型是明确的 (key: number) => string const result = req.store(123); res.send(`Get result: ${result}`); }); app.use(getRouter); app.listen(3000, () => console.log('Server running on port 3000'));
方案二:泛型中间件约束
通过泛型定义中间件的类型,让每个中间件关联特定的store类型,避免全局类型冲突:
步骤1:定义泛型中间件类型
在类型文件里定义一个泛型的中间件类型,关联自定义的store类型:
// types/middleware.ts import { RequestHandler, Request } from 'express'; // 泛型中间件类型,指定Request必须包含特定类型的store export type StoreMiddleware<T> = RequestHandler< {}, any, any, any, Request & { store: T } >;
步骤2:编写泛型中间件
a.ts里使用泛型中间件类型:
// a.ts import { Response, NextFunction } from 'express'; import { StoreMiddleware } from './types/middleware'; const setInStore = (name: string): number => { return parseInt(name, 10) || 0; }; // 明确指定泛型参数为当前store的类型 const func1: StoreMiddleware<(name: string) => number> = (req, res, next) => { req.store = (name) => setInStore(name); next(); }; export default func1;
b.ts同理:
// b.ts import { Response, NextFunction } from 'express'; import { StoreMiddleware } from './types/middleware'; const getFromStore = (key: number): string => { return `value_${key}`; }; const func2: StoreMiddleware<(key: number) => string> = (req, res, next) => { req.store = (key) => getFromStore(key); next(); }; export default func2;
步骤3:使用时推断或明确类型
在c.ts里,使用中间件后,路由处理函数的req会自动推断出store的类型(部分场景可能需要手动指定):
// c.ts import express from 'express'; import func1 from './a'; import func2 from './b'; const app = express(); app.use(func1); // 这里req的store类型会自动推断为(name: string) => number app.get('/set', (req, res) => { const result = req.store('456'); res.send(`Set result: ${result}`); }); const getRouter = express.Router(); getRouter.use(func2); // 这里req的store类型会自动推断为(key: number) => string getRouter.get('/get', (req, res) => { const result = req.store(456); res.send(`Get result: ${result}`); }); app.use(getRouter); app.listen(3000, () => console.log('Server running on port 3000'));
两种方案都能实现你的需求:使用不同中间件时,store拥有对应的类型,且不会出现全局类型冲突。
内容的提问来源于stack exchange,提问作者Saman
相关产品推荐
相关产品推荐

