我试图添加一个属性来表达使用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,
///....
});
}));
其他回答
虽然这是一个非常古老的问题,但我最近偶然发现了这个问题。接受的答案工作得很好,但我需要添加一个自定义接口请求-一个接口,我一直在我的代码中使用,并不能很好地与接受的答案。从逻辑上讲,我尝试了这样做:
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 {
...
}
只需将属性添加到req。params对象。
req.params.foo = "bar"
.d。Ts声明是黑客。简单地接受这样一个事实:express的中间件系统不适合typescript。所以不要用它。
错误代码示例:
const auth = (req) => {
const user = // parse user from the header
if(!user)
return res.status(401).send({ result: 'unauthorized-error' })
req.user = user
return next()
}
app.get('/something', auth, (req, res) => {
// do something
})
更好的代码:
const auth = (req) => {
return // parse user from the header
}
app.get('/something', (req, res) => {
const user = auth(req)
if(!user)
return res.status(401).send({ result: 'unauthorized-error' })
// do something
})
你可以使用更高阶的函数来恢复中间件的使用:
Const auth = (req) => { 从头文件返回// parse user } const withUser =(回调:(foo, req, res) => void) => (req, res) => { Const user = auth(req) 如果用户(!) 返回res.status(401)。发送({result: 'unauthorized-error'}) 返回回调(user, req, res) } app.get('/something', withUser((user, req, res) => { //做某事 }))
如果你愿意,你甚至可以把它们堆叠起来:
const withFoo = (callback) => (req, res) => { /* ... */ } const withBar = (callback) => (req, res) => { /* ... */ } const withBaz = (callback) => (req, res) => { /* ... */ } const withFooBarAndBaz = (callback) => (req,res) => { withFoo((foo) => withBar((bar) => withBaz((baz) => callback({ foo, bar, baz }, req, res) )(req,res) )(req,res) )(req,res) } app.get('/something', withFooBarAndBaz(({ foo, bar, baz }, req, res) => { // do something with foo, bar and baz }))
只要使用语言,而不是表达促进的不安全突变。
编辑:我使用第一个建议的解决方案。但不同的是,我的auth函数抛出了一个错误,我可以捕捉并返回正确的响应,所以我不需要在控制器中这样做。例如:
app.get('/something', withResponse((req) => {
const user = auth(req)
return success({
message: `Hi ${user.name}`
})
}))
我还发送返回类型,而不是手动调用res.send。它还允许我输入响应。我建议你也去查查tRPC。
一个可能的解决方案是使用“double casting to any”
用你的属性定义一个接口
export interface MyRequest extends http.IncomingMessage {
myProperty: string
}
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()
})
双铸造的优点是:
类型是可用的 它不会污染现有的定义,而是扩展了它们,避免了混淆 由于强制转换是显式的,它编译带有-noImplicitany标志的罚金
或者,还有一个快速(无类型)路由:
req['myProperty'] = setProperty()
(不要用自己的属性编辑现有的定义文件——这是不可维护的。如果定义是错误的,打开一个拉请求)
EDIT
参见下面的评论,在这种情况下,简单的强制转换工作需要MyRequest
在尝试了8个左右的答案,没有成功。我终于设法让它与jd291的评论指向3mards回购工作。
在基库中创建一个名为types/express/index.d.ts的文件。在信中写道:
declare namespace Express {
interface Request {
yourProperty: <YourType>;
}
}
并将其包含在tsconfig中。json:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
那么你的属性应该在每个请求下都是可访问的:
import express from 'express';
const app = express();
app.get('*', (req, res) => {
req.yourProperty =
});