我试图添加一个属性来表达使用typescript从中间件请求对象。但是,我不知道如何向对象添加额外的属性。如果可能的话,我宁愿不用括号。
我正在寻找一个解决方案,允许我写类似的东西(如果可能的话):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
我试图添加一个属性来表达使用typescript从中间件请求对象。但是,我不知道如何向对象添加额外的属性。如果可能的话,我宁愿不用括号。
我正在寻找一个解决方案,允许我写类似的东西(如果可能的话):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
当前回答
这招对我很管用:
declare namespace e {
export interface Request extends express.Request {
user:IUserReference,
[name:string]:any;
}
export interface Response extends express.Response {
[name:string]:any;
}
}
export type AsyncRequestHandler = (req:e.Request, res:e.Response, logger?:Logger) => Promise<any>|Promise<void>|void;
export type AsyncHandlerWrapper = (req:e.Request, res:e.Response) => Promise<void>;
我在代码中使用了它,比如以这样的方式导出具有这样签名的函数:
app.post('some/api/route', asyncHandlers(async (req, res) => {
return await serviceObject.someMethod(req.user, {
param1: req.body.param1,
paramN: req.body.paramN,
///....
});
}));
其他回答
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!'))
More
扩展全局名称空间也包含在TypeScript Deep Dive中。
可选择的解决方案
这实际上并没有直接回答这个问题,但我提供了一个替代方案。我也在与同样的问题作斗争,并尝试了该页面上几乎所有的界面扩展解决方案,但没有一个有效。
这让我停下来思考:“为什么我实际上在修改请求对象?”
使用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
}
);
这个答案将有利于那些依赖npm包ts-node的人。
我也在与扩展请求对象同样的问题作斗争,我在堆栈溢出中遵循了许多答案,并以遵循下面提到的策略结束。
我在以下目录中声明了express的扩展类型。$ {PROJECT_ROOT} / api / @types /快递/ index.d.ts
declare namespace Express {
interface Request {
decoded?: any;
}
}
然后更新我的tsconfig。Json变成这样。
{
"compilerOptions": {
"typeRoots": ["api/@types", "node_modules/@types"]
...
}
}
即使在执行上述步骤之后,visual studio也不再抱怨,但不幸的是,ts-node编译器仍然习惯抛出。
Property 'decoded' does not exist on type 'Request'.
显然,ts-node无法定位请求对象的扩展类型定义。
最终,在花了几个小时后,我知道VS Code并没有抱怨,并且能够找到类型定义,这意味着ts-node编译器有问题。
正在更新包中的开始脚本。Json为我解决了这个问题。
"start": "ts-node --files api/index.ts",
——files参数在这里起着关键作用,确定自定义类型定义。
欲了解更多信息,请访问:https://github.com/TypeStrong/ts-node#help-my-types-are-missing
这招对我很管用:
declare namespace e {
export interface Request extends express.Request {
user:IUserReference,
[name:string]:any;
}
export interface Response extends express.Response {
[name:string]:any;
}
}
export type AsyncRequestHandler = (req:e.Request, res:e.Response, logger?:Logger) => Promise<any>|Promise<void>|void;
export type AsyncHandlerWrapper = (req:e.Request, res:e.Response) => Promise<void>;
我在代码中使用了它,比如以这样的方式导出具有这样签名的函数:
app.post('some/api/route', asyncHandlers(async (req, res) => {
return await serviceObject.someMethod(req.user, {
param1: req.body.param1,
paramN: req.body.paramN,
///....
});
}));
对于较新的express版本,您需要增强express- service -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
}
}