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

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

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

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


在TypeScript中,接口是开放的。这意味着只需重新定义它们,就可以从任何地方向它们添加属性。

考虑到您正在使用这个express.d.ts文件,您应该能够重新定义Request接口以添加额外的字段。

interface Request {
  property: string;
}

然后在中间件函数中,req参数也应该具有此属性。您应该能够在不修改代码的情况下使用它。


你想要创建一个自定义定义,并使用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)
})

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中。


他们提供的解决方案没有一个对我有效。我最终只是扩展了Request接口:

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

然后使用它:

import {NextFunction, Response} from 'express';
import {RequestCustom} from 'RequestCustom';

someMiddleware(req: RequestCustom, res: Response, next: NextFunction): void
{
    req.property = '';
}

编辑:根据你的tsconfig,你可能需要这样做:

someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
    const req = expressRequest as RequestCustom;
    req.property = '';
}

公认的答案(和其他答案一样)对我不适用,但是

declare module 'express' {
    interface Request {
        myProperty: string;
    }
}

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


虽然这是一个非常古老的问题,但我最近偶然发现了这个问题。接受的答案工作得很好,但我需要添加一个自定义接口请求-一个接口,我一直在我的代码中使用,并不能很好地与接受的答案。从逻辑上讲,我尝试了这样做:

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 {
    ...
}

对于较新的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
  }
}

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

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


如果您正在寻找与express4一起工作的解决方案,下面是:

@types /快递/ index.d。--------必须是/index.d.ts

declare namespace Express { // must be namespace, and not declare module "Express" { 
  export interface Request {
    user: any;
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2016",
    "typeRoots" : [
      "@types", // custom merged types must be first in a list
      "node_modules/@types",
    ]
  }
}

参考资料来自https://github.com/TypeStrong/ts-node/issues/715#issuecomment-526757308


在尝试了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 = 
});

所有这些回答在某种程度上似乎都是错误的或过时的。

这在2020年5月对我有效:

在$ {PROJECT_ROOT} / @types /快递/ index.d.ts:

import * as express from "express"

declare global {
    namespace Express {
        interface Request {
            my_custom_property: TheCustomType
        }
    }
}

在tsconfig。Json,添加/合并属性,使:

"typeRoots": [ "@types" ]

欢呼。


这个答案将有利于那些依赖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


为了帮助那些只是在这里寻找其他尝试的人,这是我在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"
}

现在回答这个问题可能已经很晚了,但无论如何,我是这样解决的:

确保在tsconfig文件中包含了类型的源代码(这可能是一个全新的线程) 在types目录中添加一个新目录,并将其命名为要为其扩展或创建类型的包。在本例中,您将创建一个名为express的目录 在express目录中创建一个文件,并将其命名为index.d.ts(必须与此完全相同) 最后,为了扩展类型,你只需要放如下代码:

declare module 'express' {
    export interface Request {
        property?: string;
    }
}

这就是我在使用Nestjs和Express时的工作方式。截至2020年11月。

创建一个文件:./@types/express- server -static-core/index.d.ts

注意:必须有上面的路径和文件名。这样Typescript声明合并就可以了。

import { UserModel } from "../../src/user/user.model";

declare global{
    namespace Express {
        interface Request {
            currentUser: UserModel
        }
    }
}

将其添加到tsconfig.json中

"typeRoots": [
      "@types",
      "./node_modules/@types",
    ]        

在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;
}```

可选择的解决方案

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

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

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

对我来说,一个简单的解决方案就是创建一个新的自定义接口来扩展express Request。 这个接口应该包含所有可选的自定义req属性。 将此接口设置为中间件请求的类型。

// ICustomRequset.ts
   import { Request } from "express"
   export default interface ICustomRequset extends Request {
       email?: string;
       roles?: Array<string>;
   }

// AuthMiddleware.ts
...
export default async function (
  req: ICustomRequset,
  res: Response,
  next: NextFunction
) {
  try {
      // jwt code
      req.email=jwt.email
      req.roles=jwt.roles
      next()
  }catch(err){}

在2021年,这个方法是有效的:

在express 4.17.1中,https://stackoverflow.com/a/55718334/9321986和https://stackoverflow.com/a/58788706/9321986的组合工作:

在类型/快递/ index.d.ts:

declare module 'express-serve-static-core' {
    interface Request {
        task?: Task
    }
}

在tsconfig.json中:

{
    "compilerOptions": {
        "typeRoots": ["./types"]
    }
}

如果你尝试了所有的答案,仍然没有得到工作,这里有一个简单的hack

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

这将把req对象转换为任意类型,因此您可以添加任何想要的属性。 请记住,这样做将失去req对象的类型安全性。


这招对我很管用:

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,
            ///....
        });
    }));

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

req.params.foo = "bar"

我用了回应。当地人反对储存新财产。下面是完整的代码

export function testmiddleware(req: Request, res: Response, next: NextFunction) {
    res.locals.user = 1;
    next();
}

// Simple Get
router.get('/', testmiddleware, async (req: Request, res: Response) => {
    console.log(res.locals.user);
    res.status(200).send({ message: `Success! ${res.locals.user}` });
});

我也有同样的问题,我是这样解决的:

// /src/types/types.express.d.ts
declare namespace Express {
    export interface Request {
        user: IUser
    }
}

但有一些条件是必须的!

添加到tsconfig。json配置

"paths": {
    "*": [
        "node_modules/*",
        "src/types/*"
    ]
},

在此之后,tsc将构建bundle,而ts-node则不会。

必须在编译器命令中添加——files

ts-node --files src/server.ts

我通过创建一个新类型而没有全局扩展Request类型来解决这个问题。

import { Request } from 'express'
    
type CustomRequest = Request & { userId?: string }

必须使用带有可选(?)操作符的扩展属性,以避免出现“没有重载匹配此调用”错误。

软件包版本:

    "@types/express": "^4.17.13",
    "@types/morgan": "^1.9.3",
    "@types/node": "^17.0.29",
    "typescript": "^4.6.3",
    "express": "^4.18.0",

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

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

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

缺点:

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


我已经改变了响应类型,包括ApiResponse(一个自定义响应对象)response <ApiResponse>

export interface ApiResponse {
    status?: string
    error?: string
    errorMsg?: string
    errorSubject?: string
    response?: any
}

const User = async (req: Request, res: Response<ApiResponse>, next: NextFunction) => {
    try {
        if (!username) return res.status(400).send({ errorMsg: 'Username is empty' })
        /* ... */
    } catch(err){
        /* ... */
    }
}

.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。