Isa*_*ren 78 node.js express typescript
我正在尝试使用typescript添加一个属性来表示来自中间件的请求对象.但是,我无法弄清楚如何向对象添加额外的属性.如果可能的话,我宁愿不使用括号表示法.
我正在寻找一种解决方案,允许我写一些类似的东西(如果可能的话):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
Run Code Online (Sandbox Code Playgroud)
小智 96
您想要创建自定义定义,并在Typescript中使用名为声明合并的功能.这通常用于例如method-override
.
创建一个文件custom.d.ts
,并确保将其包含在你tsconfig.json
的部分中(files
如果有的话).内容如下:
declare namespace Express {
export interface Request {
tenant?: string
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许您在代码中的任何位置使用以下内容:
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)
})
Run Code Online (Sandbox Code Playgroud)
kal*_*ave 55
在尝试了 8 个左右的答案但没有成功之后。我终于设法让它与jd291指向3mards repo的评论一起工作。
在库中创建一个名为types/express/index.d.ts
. 并在其中写道:
declare namespace Express {
interface Request {
yourProperty: <YourType>;
}
}
Run Code Online (Sandbox Code Playgroud)
并将其包含在tsconfig.json
:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
Run Code Online (Sandbox Code Playgroud)
然后yourProperty
应该可以在每个请求下访问:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
Run Code Online (Sandbox Code Playgroud)
bas*_*rat 50
您只需向全局index.d.ts
命名空间声明任何新成员.例:
declare global {
namespace Express {
interface Request {
context: Context
}
}
}
Run Code Online (Sandbox Code Playgroud)
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!'))
Run Code Online (Sandbox Code Playgroud)
此处介绍了扩展全局命名空间:https://basarat.gitbooks.io/typescript/docs/types/lib.d.ts.html
anl*_*nli 41
在 Express 4.17.1 中, /sf/answers/3900283411/和/sf/answers/4115209451/的组合有效:
在types/express/index.d.ts
:
declare module 'express-serve-static-core' {
interface Request {
task?: Task
}
}
Run Code Online (Sandbox Code Playgroud)
并在tsconfig.json
:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句:如果你想这样做,请三思!当我不了解 TDD 时我就这样做了 - 现在我无法想象在日常工作中不使用代码测试 - 并且“污染”请求对象并让它在应用程序中徘徊会增加您需要的机会模拟大量测试的要求。也许最好阅读Clean Code、SOLID、IOSP、IOSP 2.0和IODA以及类似的内容,并理解为什么向请求添加属性以使其在“应用程序的另一端”可用可能不是最好的主意。
max*_*-lt 21
接受的答案(和其他人一样)对我不起作用
declare module 'express' {
interface Request {
myProperty: string;
}
}
Run Code Online (Sandbox Code Playgroud)
没有.希望能帮到别人.
JCM*_*JCM 18
对于较新版本的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
}
}
Run Code Online (Sandbox Code Playgroud)
Wil*_*ner 17
所有这些回应似乎在某种程度上都是错误的或过时的。
这在 2020 年 5 月对我有用:
在${PROJECT_ROOT}/@types/express/index.d.ts
:
import * as express from "express"
declare global {
namespace Express {
interface Request {
my_custom_property: TheCustomType
}
}
}
Run Code Online (Sandbox Code Playgroud)
在 中tsconfig.json
,添加/合并属性,以便:
"typeRoots": [ "@types" ]
Run Code Online (Sandbox Code Playgroud)
干杯。
小智 16
我通过创建一个新类型解决了这个问题,而没有全局扩展 Request 类型。
import { Request } from 'express'
type CustomRequest = Request & { userId?: string }
Run Code Online (Sandbox Code Playgroud)
您必须将扩展属性与可选(?)运算符一起使用,以免出现“没有与此调用匹配的重载”错误。
封装版本:
"@types/express": "^4.17.13",
"@types/morgan": "^1.9.3",
"@types/node": "^17.0.29",
"typescript": "^4.6.3",
"express": "^4.18.0",
Run Code Online (Sandbox Code Playgroud)
Div*_*wat 13
这个答案将对那些依赖 npm 包的人有益ts-node
。
我也在努力解决扩展请求对象的同样问题,我遵循了堆栈溢出中的很多答案,并最终遵循了下面提到的策略。
我在以下目录中声明了Express的扩展类型。${PROJECT_ROOT}/api/@types/express/index.d.ts
declare namespace Express {
interface Request {
decoded?: any;
}
}
Run Code Online (Sandbox Code Playgroud)
然后将我的更新tsconfig.json
为类似的内容。
{
"compilerOptions": {
"typeRoots": ["api/@types", "node_modules/@types"]
...
}
}
Run Code Online (Sandbox Code Playgroud)
即使完成上述步骤后,Visual Studio 也不再抱怨,但不幸的是,ts-node
编译器仍然习惯抛出异常。
Property 'decoded' does not exist on type 'Request'.
Run Code Online (Sandbox Code Playgroud)
显然,ts-node
无法找到请求对象的扩展类型定义。
最终,在花了几个小时之后,我知道 VS Code 没有抱怨并且能够找到类型定义,这意味着ts-node
编译器出了问题。
更新开始为我修复script
了package.json
它。
"start": "ts-node --files api/index.ts",
Run Code Online (Sandbox Code Playgroud)
参数--files
在这里起着关键作用,以确定自定义类型定义。
欲了解更多信息,请访问:https://github.com/TypeStrong/ts-node#help-my-types-are-missing
16k*_*6kb 11
虽然这是一个非常古老的问题,但我最近偶然发现了这个问题。接受的答案工作正常,但我需要添加一个自定义界面Request
- 我在我的代码中一直使用的界面,但与接受的界面效果不佳回答。从逻辑上讲,我试过这个:
import ITenant from "../interfaces/ITenant";
declare namespace Express {
export interface Request {
tenant?: ITenant;
}
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为 Typescript 将.d.ts
文件视为全局导入,并且当它们中有导入时,它们被视为普通模块。这就是为什么上面的代码不适用于标准的打字稿设置。
这是我最终做的
// typings/common.d.ts
declare namespace Express {
export interface Request {
tenant?: import("../interfaces/ITenant").default;
}
}
Run Code Online (Sandbox Code Playgroud)
// interfaces/ITenant.ts
export interface ITenant {
...
}
Run Code Online (Sandbox Code Playgroud)
Aaa*_*ron 11
This is not actually answering to the question directly, but I'm offering an alternative. I was struggling with the same problem and tried out pretty much every interface extending solution on this page and none of them worked.
That made me stop to think: "Why am I actually modifying the request object?".
response.locals
Express developers seem to have thought that users might want to add their own properties. That's why there is a locals
object. The catch is, that it's not in the request
but in the response
object.
The response.locals
object can contain any custom properties you might want to have, encapsulated in the request-response cycle, thus not exposed to other requests from different users.
Need to store an userId? Just set response.locals.userId = '123'
. No need to struggle with the typings.
The downside of it is that you have to pass the response object around, but it's very likely that you are doing it already.
https://expressjs.com/en/api.html#res.locals
Another downside is the lack of type safety. You can, however, use the generic types on the Response object to define what's the structure of the body
and the locals
objects:
Response<MyResponseBody, MyResponseLocals>
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L127
You cannot really guarantee that the userId property is actually there. You might want to check before accessing it, especially in case of objects.
Using the example above to add an userId, we could have something like this:
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
}
);
Run Code Online (Sandbox Code Playgroud)
提供的解决方案均不适合我。我最终只是扩展了Request接口:
import {Request} from 'express';
export interface RequestCustom extends Request
{
property: string;
}
Run Code Online (Sandbox Code Playgroud)
然后使用它:
import {NextFunction, Response} from 'express';
import {RequestCustom} from 'RequestCustom';
someMiddleware(req: RequestCustom, res: Response, next: NextFunction): void
{
req.property = '';
}
Run Code Online (Sandbox Code Playgroud)
编辑:TypeScript的最新版本对此有所抱怨。相反,我必须这样做:
someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
const req = expressRequest as RequestCustom;
req.property = '';
}
Run Code Online (Sandbox Code Playgroud)
在TypeScript中,接口是开放式的.这意味着只需重新定义属性,就可以从任何地方为它们添加属性.
考虑到您正在使用此express.d.ts文件,您应该能够重新定义Request接口以添加额外字段.
interface Request {
property: string;
}
Run Code Online (Sandbox Code Playgroud)
然后在您的中间件函数中,req参数也应具有此属性.您应该能够在不对代码进行任何更改的情况下使用它.
如果您正在寻找适用于 express4 的解决方案,这里是:
@types/express/index.d.ts: --------必须是/index.d.ts
declare namespace Express { // must be namespace, and not declare module "Express" {
export interface Request {
user: any;
}
}
Run Code Online (Sandbox Code Playgroud)
tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"typeRoots" : [
"@types", // custom merged types must be first in a list
"node_modules/@types",
]
}
}
Run Code Online (Sandbox Code Playgroud)
参考来自https://github.com/TypeStrong/ts-node/issues/715#issuecomment-526757308
小智 6
如果您尝试了所有答案但仍然无法正常工作,这里有一个简单的技巧
app.use((req, res, next) => {
(req as any).property = setProperty();
next();
});
Run Code Online (Sandbox Code Playgroud)
这会将req
对象转换为any
,因此您可以添加所需的任何属性。请记住,这样做您将失去req
对象的类型安全性。
一种可能的解决方案是使用“双重转换到任何”
1-定义与您的属性的接口
export interface MyRequest extends http.IncomingMessage {
myProperty: string
}
Run Code Online (Sandbox Code Playgroud)
2-双铸
app.use((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: Error) => void) => {
const myReq: MyRequest = req as any as MyRequest
myReq.myProperty = setProperty()
next()
})
Run Code Online (Sandbox Code Playgroud)
双铸件的优点是:
-noImplicitany
标志编译罚款或者,还有快速(无类型)路线:
req['myProperty'] = setProperty()
Run Code Online (Sandbox Code Playgroud)
(不要使用您自己的属性编辑现有定义文件 - 这是无法维护的。如果定义错误,请打开拉取请求)
编辑
请参阅下面的评论,在这种情况下可以进行简单的铸造req as MyRequest
也许这个问题已经得到解答,但我想分享一点,现在有时像其他答案一样的接口可能有点过于严格,但我们实际上可以维护所需的属性,然后通过创建一个来添加任何其他属性键的类型string
为 值的类型为any
import { Request, Response, NextFunction } from 'express'
interface IRequest extends Request {
[key: string]: any
}
app.use( (req: IRequest, res: Response, next: NextFunction) => {
req.property = setProperty();
next();
});
Run Code Online (Sandbox Code Playgroud)
现在,我们还可以向该对象添加我们想要的任何附加属性。
为了帮助那些只是想在这里尝试其他东西的人,这对我在 2020 年 5 月下旬尝试扩展 ExpressJS 的请求时起了作用。在让它发挥作用之前,我必须尝试十几件事:
"typeRoots": [
"./node_modules/@types",
"./your-custom-types-dir"
]
Run Code Online (Sandbox Code Playgroud)
declare global {
namespace Express {
interface Request {
customBasicProperty: string,
customClassProperty: import("../path/to/CustomClass").default;
}
}
}
Run Code Online (Sandbox Code Playgroud)
{
"restartable": "rs",
"ignore": [".git", "node_modules/**/node_modules"],
"verbose": true,
"exec": "ts-node --files",
"watch": ["src/"],
"env": {
"NODE_ENV": "development"
},
"ext": "js,json,ts"
}
Run Code Online (Sandbox Code Playgroud)
这就是使用 Nestjs 和 Express 时对我有用的方法。与 2020 年 11 月一样。
创建文件:./@types/express-serve-static-core/index.d.ts
注意:必须与上述路径和文件名完全一致。这样 Typescript 声明合并就可以工作了。
import { UserModel } from "../../src/user/user.model";
declare global{
namespace Express {
interface Request {
currentUser: UserModel
}
}
}
Run Code Online (Sandbox Code Playgroud)
将此添加到您的 tsconfig.json
"typeRoots": [
"@types",
"./node_modules/@types",
]
Run Code Online (Sandbox Code Playgroud)
我有同样的问题并这样解决:
// /src/types/types.express.d.ts
declare namespace Express {
export interface Request {
user: IUser
}
}
Run Code Online (Sandbox Code Playgroud)
但需要一些条件!
tsconfig.json
配置"paths": {
"*": [
"node_modules/*",
"src/types/*"
]
},
Run Code Online (Sandbox Code Playgroud)
之后tsc
将构建捆绑包,但ts-node
不会。
--files
到编译器命令ts-node --files src/server.ts
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
34401 次 |
最近记录: |