我正在尝试使用typescript在中间件中添加一个表示请求对象的属性。但是我无法弄清如何向该对象添加额外属性。如果可能,我希望不使用方括号表示。
我正在寻找一种解决方案,使我可以编写类似于以下内容的代码(如果可能):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
我正在尝试使用typescript在中间件中添加一个表示请求对象的属性。但是我无法弄清如何向该对象添加额外属性。如果可能,我希望不使用方括号表示。
我正在寻找一种解决方案,使我可以编写类似于以下内容的代码(如果可能):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
你想创建一个自定义定义,并使用TypeScript中的一个特性,称为声明合并。这通常在method-override
等项目中使用。
创建一个名为custom.d.ts
的文件,并确保将其包含在tsconfig.json
文件的files
部分(如果有的话)中。内容如下:
declare namespace Express {
export interface Request {
tenant?: string
}
}
这将允许您在代码的任何位置使用以下内容:
router.use((req, res, next) => {
req.tenant = 'tenant-X'
next()
})
router.get('/whichTenant', (req, res) => {
res.status(200).send('This is your tenant: '+req.tenant)
})
Property 'tenant' does not exist on type 'Request'
的错误。无论我是否在 tsconfig.json
中明确包含它都没有任何区别。更新,如@basarat 在他的答案中指出的那样,使用 declare global
可行,但我必须首先执行 import {Request} from 'express'
。 - LionRequest
对象的正确方法。 - Eric Liprandi根据在index.d.ts
的注释中所建议的,您只需向全局Express
命名空间声明任何新成员。例如:
declare global {
namespace Express {
interface Request {
context: Context
}
}
}
import * as express from 'express';
export class Context {
constructor(public someContextVariable) {
}
log(message: string) {
console.log(this.someContextVariable, { message });
}
}
declare global {
namespace Express {
interface Request {
context: Context
}
}
}
const app = express();
app.use((req, res, next) => {
req.context = new Context(req.url);
next();
});
app.use((req, res, next) => {
req.context.log('about to return')
res.send('hello world world');
});
app.listen(3000, () => console.log('Example app listening on port 3000!'))
在TypeScript深入探讨中也介绍了扩展全局命名空间的内容。
对于较新版本的express,您需要扩充express-serve-static-core
模块。
这是必要的,因为现在Express对象来自那里:https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19
基本上,使用以下内容:
declare module 'express-serve-static-core' {
interface Request {
myField?: string
}
interface Response {
myField?: string
}
}
'express'
模块则无效。谢谢! - Ben Kreegerimport {Express} from "express-serve-static-core";
- andre_bimport
语句将文件转换为模块,这是必要的部分。我已经切换到使用export {}
也可以工作。 - Danyal Aytekinexpress.d.ts
,否则编译器会尝试将其与 express 类型合并,导致出现错误。请注意不要改变原文意思。 - Tom Spencertypes/express/index.d.ts
的文件,然后在其中编写以下内容:declare namespace Express {
interface Request {
yourProperty: <YourType>;
}
}
并且在 tsconfig.json
中加入以下内容:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
然后,yourProperty
应该在每个请求下都可以访问:
import express from 'express';
const app = express();
app.get('*', (req, res) => {
req.yourProperty =
});
declare global {}
中包裹该命名空间,这样就能正常工作了。 - Matthew Goodwin所有提供的解决方案对我都不起作用。最后我简单地扩展了Request接口:
import {Request} from 'express';
export interface RequestCustom extends Request
{
property: string;
}
然后使用它:
import {NextFunction, Response} from 'express';
import {RequestCustom} from 'RequestCustom';
someMiddleware(req: RequestCustom, res: Response, next: NextFunction): void
{
req.property = '';
}
编辑:根据你的tsconfig,你可能需要这样做:
someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
const req = expressRequest as RequestCustom;
req.property = '';
}
interface CustomResponse extends Response { customProperty: string}
,在调用它时someMiddleware(res: CustomResponse)
,它可以正常工作,并且可以访问存在于Response和新定义接口上的属性。 - Petar接受的答案(像其他答案一样)对我没有用,但是
declare module 'express' {
interface Request {
myProperty: string;
}
}
做了。希望能帮到某个人。
custom-declarations.d.ts
文件放在 TypeScript 项目的根目录中。 - focornerimport { Request as IRequest } from 'express/index';
和 interface Request extends IRequest
。还必须添加 typeRoot。 - Ben Creasytypes/express/index.d.ts
中:declare module 'express-serve-static-core' {
interface Request {
task?: Task
}
}
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
TSError: ⨯ 无法编译TypeScript
。 - charliebrownie实际上这并不是直接回答问题,但我提供了一种替代方案。我也曾遇到同样的问题,尝试了此页面上几乎所有扩展界面的解决方法,但没有一个有效。
这让我停下来思考:"我为什么要修改请求对象呢?"。
response.locals
Express开发人员似乎已经认为用户可能希望添加自己的属性,这就是为什么有一个locals
对象。问题是,它不在request
中,而是在response
对象中。
response.locals
对象可以包含任何您想要拥有的自定义属性,封装在请求-响应周期中,因此不会暴露给来自不同用户的其他请求。
需要存储userId吗?只需设置response.locals.userId = '123'
。无需苦恼于类型。
缺点是您必须在各处传递响应对象,但很可能您已经这样做了。
https://expressjs.com/en/api.html#res.locals
另一个缺点是缺乏类型安全性。然而,您可以使用响应对象上的通用类型来定义body
和locals
对象的结构:
Response<MyResponseBody,MyResponseLocals>
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L127
您实际上无法保证userId属性是否存在。在访问之前,特别是在对象的情况下,您可能需要进行检查。
使用上面的示例添加userId,我们可能会得到这样的结果:
interface MyResponseLocals {
userId: string;
}
const userMiddleware = (
request: Request,
response: Response<MyResponseBody, MyResponseLocals>,
next: NextFunction
) => {
const userId: string = getUserId(request.cookies.myAuthTokenCookie);
// Will nag if you try to assign something else than a string here
response.locals.userId = userId;
next();
};
router.get(
'/path/to/somewhere',
userMiddleware,
(request: Request, response: Response<MyResponseBody, MyResponseLocals>) => {
// userId will have string type instead of any
const { userId } = response.locals;
// You might want to check that it's actually there
if (!userId) {
throw Error('No userId!');
}
// Do more stuff
}
);
response.locals
仍然没有类型。存储在其中的任何值都是 any
。 - Martti Laine所有这些回答似乎都存在错误或已过时。
以下内容于2020年5月对我有效:
在${PROJECT_ROOT}/@types/express/index.d.ts
文件中:
import * as express from "express"
declare global {
namespace Express {
interface Request {
my_custom_property: TheCustomType
}
}
}
在tsconfig.json
中添加/合并属性,使其如下:
"typeRoots": [ "@types" ]
干杯。
Request
类型上不存在user
属性。 - Oliver Dixon虽然这是一个非常古老的问题,但我最近遇到了这个问题。已接受的答案可以正常工作,但我需要添加自定义接口到Request
- 一个我在我的代码中一直在使用并且与已接受的答案不太兼容的接口。从逻辑上讲,我尝试了这个:
import ITenant from "../interfaces/ITenant";
declare namespace Express {
export interface Request {
tenant?: ITenant;
}
}
但这并不起作用,因为Typescript将 .d.ts
文件视为全局导入,在其中有导入时会将其视为普通模块。这就是为什么上面的代码在标准typescript设置下不起作用。
下面是我最后采取的方法
// typings/common.d.ts
declare namespace Express {
export interface Request {
tenant?: import("../interfaces/ITenant").default;
}
}
// interfaces/ITenant.ts
export interface ITenant {
...
}
.d.ts
、tsconfig.json
以及使用实例吗?另外,你在使用哪个版本的TypeScript?因为只有从TS 2.9开始才支持在全局模块中进行导入。这些信息可能会更有帮助。 - 16kb... "include": ["src/**/*"] ...
是有效的,但"include": ["./src/", "./src/Types/*.d.ts"]
却无效。 我还没有深入尝试理解这个问题。 - 16kb