如何在Express中全局扩展Request对象类型,使自定义属性$mappedBody获得TypeScript自动补全?
我太懂你这种烦恼了——每次为了用自定义的$mappedBody属性都要手动写接口或者类型断言,不仅麻烦还容易出错。其实TypeScript的**模块扩展(Module Augmentation)**特性正好能解决这个问题,让我们全局给Express的Request对象添加上自定义属性的类型,之后整个项目里的路由和中间件都能自动识别这个属性,不用再重复定义啦!
步骤1:创建TypeScript声明文件
首先在你的项目里新建一个类型声明文件,推荐放在src/@types/express/index.d.ts(这个路径是TypeScript默认会扫描的自定义类型目录,当然你也可以放在其他地方,只要确保tsconfig能识别到)。
步骤2:扩展Express的Request接口
在这个声明文件里,我们要借助TypeScript的模块扩展语法,给Express的Request接口添加$mappedBody属性的类型。代码示例如下:
// src/@types/express/index.d.ts import { Request } from 'express'; // 先定义你$mappedBody的类型,比如键值都是字符串的对象 type MappedBody = Record<string, string>; // 扩展Express的Request接口 declare module 'express' { interface Request { // 如果这个属性是所有经过中间件的请求都必有的,就不用加?;如果是可选的(比如部分路由不用这个中间件),就加? $mappedBody: MappedBody; // 或者如果你的mapFields是泛型函数,想让类型更灵活,可以写成: // $mappedBody?: Record<string, string>; } }
这里解释一下:如果你的mapFields中间件是在所有路由都用的,那$mappedBody就是必选的;如果只是部分路由用,那最好加上?变成可选属性,避免在没用到中间件的路由里TypeScript报错。
步骤3:确保TypeScript识别这个声明文件
打开你的tsconfig.json,检查include配置里是否包含了这个声明文件的路径。比如如果你的声明文件在src/@types目录下,那include应该像这样:
{ "compilerOptions": { // ...其他配置 }, "include": [ "src/**/*", "src/@types/**/*" // 确保包含这个路径 ] }
如果你的声明文件是直接放在src根目录下的express.d.ts,那只要include包含src/**/*就可以了。
步骤4:验证效果
现在回到你的代码里,不管是中间件还是路由处理函数,只要是经过mapFields中间件的请求,TypeScript都会自动识别req.$mappedBody属性,并且提供对应的类型补全:
// app.ts import express from 'express'; const app = express(); const mapFields = () => (req, res, next) => { // 你的键值互换逻辑 req.$mappedBody = Object.fromEntries( Object.entries(req.body).map(([key, value]) => [value, key]) ); next(); }; app.post('/test', mapFields(), (req, res) => { // 这里TypeScript会自动提示req.$mappedBody的属性,并且不会报错 console.log(req.$mappedBody.someKey); res.send('OK'); });
额外提示
如果你的mapFields是泛型函数,能根据输入的类型生成对应的$mappedBody类型,那可以把扩展的接口也改成泛型吗?其实也可以,但稍微复杂一点。不过如果只是简单的键值互换(字符串键值),用Record<string, string>就足够了。如果有特定的业务类型,比如你知道$mappedBody的结构,也可以直接定义成对应的接口:
interface UserMappedBody { username: string; email: string; } declare module 'express' { interface Request { $mappedBody?: UserMappedBody; } }
这样之后,你就不用再每次手动写createUserRequest这种接口了,全局一次配置,整个项目都能享受自动补全!
备注:内容来源于stack exchange,提问作者Islam Y-

