我试图添加一个属性来表达使用typescript从中间件请求对象。但是,我不知道如何向对象添加额外的属性。如果可能的话,我宁愿不用括号。

我正在寻找一个解决方案,允许我写类似的东西(如果可能的话):

app.use((req, res, next) => {
    req.property = setProperty(); 
    next();
});

当前回答

你想要创建一个自定义定义,并使用Typescript中称为声明合并的特性。这是常用的,例如在方法重写中。

创建一个custom.d.ts文件,并确保将其包含在tsconfig中。Json的文件节(如果有的话)。内容如下所示:

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)
})

其他回答

也许这个问题已经有了答案,但我想分享一点, 有时候,像其他答案这样的界面可能有点太严格了, 但我们实际上可以维护所需的属性,然后通过创建值为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();
});

现在,我们还可以向这个对象添加任何我们想要的属性。

为了帮助那些只是在这里寻找其他尝试的人,这是我在2020年5月底试图扩展ExpressJS的Request时所做的工作。我不得不尝试了十几件事,才让这个工作:

在tsconfig的“typeRoots”中翻转每个人推荐的顺序。如果你在tsconfig中有一个rootDir设置,比如"./src",不要忘记删除src路径。例子:

"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]

自定义扩展('./your-custom-types-dir/express/index.d.ts")的例子。在我的经验中,我不得不使用内联导入和默认导出来使用类作为类型,所以这也显示出来了:

declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}

更新你的nodemon。Json文件,将“——files”命令添加到ts-node,示例:

{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}

可选择的解决方案

这实际上并没有直接回答这个问题,但我提供了一个替代方案。我也在与同样的问题作斗争,并尝试了该页面上几乎所有的界面扩展解决方案,但没有一个有效。

这让我停下来思考:“为什么我实际上在修改请求对象?”

使用response.locals

Express开发人员似乎认为用户可能希望添加自己的属性。这就是为什么有一个locals对象。问题是,它不在请求中,而是在响应对象中。

响应。Locals对象可以包含您可能希望拥有的任何自定义属性,这些属性封装在请求-响应周期中,因此不会暴露给来自不同用户的其他请求。

需要存储userId?只需设置response.locals.userId = '123'。没有必要纠结于打字。

它的缺点是,您必须传递响应对象,但很可能您已经这样做了。

https://expressjs.com/en/api.html#res.locals

打字

另一个缺点是缺乏类型安全性。然而,你可以在Response对象上使用泛型类型来定义body和locals对象的结构:

响应< 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
  }
);

只需将属性添加到req。params对象。

req.params.foo = "bar"

在mac节点12.19.0和express 4上,使用护照进行身份验证,我需要扩展我的Session对象

与上面相似,但略有不同:

import { Request } from "express";


declare global {
  namespace Express {
    export interface Session {
      passport: any;
      participantIds: any;
      uniqueId: string;
    }
    export interface Request {
      session: Session;
    }
  }
}

export interface Context {
  req: Request;
  user?: any;
}```