Node.js 使用Typescript扩展Express请求对象

Node.js 使用Typescript扩展Express请求对象,node.js,express,typescript,Node.js,Express,Typescript,我正在尝试添加一个属性,以使用typescript从中间件表达请求对象。但是,我不知道如何向对象添加额外的属性。如果可能的话,我宁愿不使用括号表示法 我正在寻找一个解决方案,允许我写类似的东西(如果可能): 一种可能的解决方案是使用“对任何对象进行双重转换” 1-定义与属性的接口 export interface MyRequest extends http.IncomingMessage { myProperty: string } 2-双重铸造 app.use((req: htt

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

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


一种可能的解决方案是使用“对任何对象进行双重转换”

1-定义与属性的接口

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
    标志编译细粒
或者,还有快速(非类型化)路线:

(不要用自己的属性编辑现有定义文件-这是不可维护的。如果定义错误,请打开拉取请求)

编辑


请参阅下面的注释,在这种情况下,简单的强制转换可以作为MyRequest工作,在TypeScript中,接口是开放式的。这意味着您只需重新定义属性,就可以从任何位置向其添加属性

考虑到您正在使用此文件,您应该能够重新定义请求接口以添加额外字段

接口请求{
属性:字符串;
}

然后在中间件函数中,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)
})
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
命名空间声明任何新成员。例如:

declare global {
  namespace Express {
    interface Request {
      context: Context
    }
  }
}
完整示例:
扩展全局名称空间也包括在内。

提供的解决方案中没有一个适合我。我最终只是扩展了请求接口:

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

是的。希望这会对某人有所帮助。

虽然这是一个非常老的问题,但我最近偶然发现了这个问题。接受的答案可以正常工作,但我需要为
请求添加一个自定义接口
——我在代码中使用的一个接口,与接受的答案不太匹配。从逻辑上讲,我试过:

从“./interfaces/ITenant”导入ITenant;
声明命名空间表达式{
导出接口请求{
租户?:ITenant;
}
}
但这不起作用,因为Typescript将
.d.ts
文件视为全局导入,当它们包含导入时,它们被视为普通模块。这就是为什么上面的代码不能在标准的排版脚本设置上工作

这就是我最后做的

//打字/common.d.ts
声明命名空间表达式{
导出接口请求{
租户?:导入(“../interfaces/ITenant”)。默认值;
}
}
//接口/ITenant.ts
导出接口ITenant{
...
}

对于较新版本的express,您需要扩展
express服务静态核心模块

这是必需的,因为现在Express对象来自于此:

基本上,使用以下方法:

declare module 'express-serve-static-core' {
  interface Request {
    myField?: string
  }
  interface Response {
    myField?: string
  }
}

也许这个问题已经得到了回答,但我想和大家分享一点, 现在,有时候像其他答案一样的界面可能有点过于严格, 但是我们实际上可以维护所需的属性,然后通过创建一个类型为
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一起使用的解决方案,请参阅:

@类型/express/index.d.ts:-----必须是/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",
    ]
  }
}

在尝试了8个左右的答案但没有成功后,请参考。我终于设法让它工作起来了,他的评论指向了我

在库中创建一个名为
types/express/index.d.ts
的文件。在信中写道:

declare namespace Express{
接口请求{
你的财产:;
}
}
并将其包含在
tsconfig.json
中,包括:

{
    "compilerOptions": {
        "typeRoots": ["./types"]
    }
}
然后在每个请求下都应该可以访问
yourProperty

从“express”导入express;
常量app=express();
应用程序获取(“*”,(请求,请求)=>{
req.yourProperty=
});

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

这在2020年5月对我起了作用:

${PROJECT_ROOT}/@types/express/index.d.ts
中:

import * as express from "express"

declare global {
    namespace Express {
        interface Request {
            my_custom_property: TheCustomType
        }
    }
}
declare module 'express-serve-static-core' {
    interface Request {
        task?: Task
    }
}
tsconfig.json
中,添加/合并属性,以便:

"typeRoots": [ "@types" ]

干杯。

这个答案将对那些依赖npm包
ts节点的人有益

我也在为扩展请求对象的问题而挣扎,我在stack overflow中遵循了很多答案,最后遵循了下面提到的策略

我在下面的目录中为express声明了扩展键入<代码>${PROJECT_ROOT}/api/@types/express/index.d.ts

declare namespace Express {
  interface Request {
    decoded?: any;
  }
}
然后将我的
tsconfig.json
更新为类似的内容

{
  "compilerOptions": {
     "typeRoots": ["api/@types", "node_modules/@types"]
      ...
  }
}
即使完成了上述步骤,VisualStudio也停止了抱怨,但不幸的是,
ts节点
编译器仍然使用抛出

 Property 'decoded' does not exist on type 'Request'.
显然,
ts节点
无法定位请求对象的扩展类型定义

最终在sp之后
 Property 'decoded' does not exist on type 'Request'.
"start": "ts-node --files api/index.ts",
import { UserModel } from "../../src/user/user.model";

declare global{
    namespace Express {
        interface Request {
            currentUser: UserModel
        }
    }
}
"typeRoots": [
      "@types",
      "./node_modules/@types",
    ]        
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;
}```
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
  }
);
// 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){}
declare module 'express-serve-static-core' {
    interface Request {
        task?: Task
    }
}
{
    "compilerOptions": {
        "typeRoots": ["./types"]
    }
}
app.use((req, res, next) => {
    (req as any).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,
            ///....
        });
    }));