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

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

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

当前回答

这个答案将有利于那些依赖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 module 'express' {
    interface Request {
        myProperty: string;
    }
}

所做的。希望这能帮助到一些人。

可选择的解决方案

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

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

使用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
  }
);

对于简单的情况,我在外部中间件中使用headers属性,然后在内部中间件中获得它。

// outer middleware
req.headers["custom_id"] = "abcxyz123";

// inner middleware
req.get("custom_id");

缺点:

只能存储字符串。如果您想存储其他类型,如json或number,您可能必须稍后解析它。 headers属性没有文档化。Express只记录了req.get()方法。因此,您必须使用与属性头文件一起工作的Express的确切版本。

一个可能的解决方案是使用“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

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

req.params.foo = "bar"