无法在 TypeScript 中扩展 Express 请求

Posted

技术标签:

【中文标题】无法在 TypeScript 中扩展 Express 请求【英文标题】:Unable to extend Express Request in TypeScript 【发布时间】:2019-05-30 12:29:11 【问题描述】:

这个问题已经被问了好几次了,但是给出的答案都没有对我有用。我正在尝试扩展 Express Request 对象以包含一个用于存储 User 对象的属性。我创建了一个声明文件express.d.ts,并将其放在与我的tsconfig.json相同的目录中:

import  User  from "./src/models/user";

declare namespace Express 
    export interface Request 
        user: User;
    

然后我尝试在secured-api.ts 中对其进行分配:

import express from 'express';
import  userService  from '../services/user';

router.use(async (req, res, next) => 
    try 
        const user = await userService.findByUsername(payload.username);

        // do stuff to user...

        req.user = user;
        next();
     catch(err) 
        // handle error
    
);

我收到以下错误:

src/routes/secured-api.ts:38:21 - error TS2339: Property 'user' does not exist on type 'Request'.

38                 req.user = user;
                       ~~~~

我的User 班级是:

import  Model, RelationMappings  from 'objection';

export class User extends Model 

    public static tableName = 'User';
    public static idColumn = 'username';

    public static jsonSchema = 
        type: 'object',
        required: ['fname', 'lname', 'username', 'email', 'password'],

        properties: 
            fname:  type: 'string', minLength: 1, maxLength: 30 ,
            lname:  type: 'string', minLength: 1, maxLength: 30 ,
            username:  type: 'string', minLength: 1, maxLength: 20 ,
            email:  type: 'string', minLength: 1, maxLength: 320 ,
            password:  type: 'string', minLength: 1, maxLength: 128 ,
        
    ;

    public static modelPaths = [__dirname];

    public static relationMappings: RelationMappings = 

    ;

    public fname!: string;
    public lname!: string;
    public username!: string;
    public email!: string;
    public password!: string;

我的tsconfig.json 是:


    "compilerOptions": 
      "target": "es6",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
      "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
      "lib": ["es2015"],                             /* Specify library files to be included in the compilation. */
      "outDir": "./build",                      /* Redirect output structure to the directory. */
      "strict": true,                           /* Enable all strict type-checking options. */
      "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    

我的目录结构是:

backend/
    package.json
    tsconfig.json
    express.d.ts
    src/
        models/
            user.ts
        routes/
            secured-api.ts

我在这里做错了什么?

【问题讨论】:

【参考方案1】:

问题在于您没有扩充由express 定义的Express 全局命名空间,而是在您的模块中创建了一个新的命名空间(一旦您使用import,该文件就会成为一个模块)。

解决办法是在global中声明命名空间

import  User  from "./src/models/user";

declare global 
    namespace Express 
        export interface Request 
            user: User;
        
    

或者不使用模块导入语法,只引用类型:

declare namespace Express 
    export interface Request 
        user: import("./src/models/user").User;
    

【讨论】:

非常感谢!我将declare global 块放在express.d.ts 中,我必须从declare namespace Express 中删除declare 才能让它工作,但它现在可以工作了。 @user3814613 wops 忘记了其中的额外声明,将其删除。对不起 好心的先生。有什么办法可以用 tsc 自动完成吗?也许当我的src 文件夹中有express_ext.d.ts 时,express_ext.d.ts 的内容会自动在index.d.ts 中进行全局声明? namespace Express 似乎对我不起作用。我不得不改用declare module 'express'。知道为什么会这样吗? declare namespace Express 解决方案在我在express/ 目录中创建index.d.ts 文件后起作用。【参考方案2】:

我也遇到了同样的问题...

您不能只使用扩展请求吗? 喜欢:

interface RequestWithUser extends Request 
    user?: User;

router.post('something',(req: RequestWithUser, res: Response, next)=>
   const user = req.user;
   ...

另一方面,如果您将异步回调与 express 一起使用,请确保使用 express-async 包装器或确保您确切知道自己在做什么。我推荐:awaitjs

【讨论】:

如果user 不是可选的,则不确定是否可以与strictFunctionTypes 一起使用,因为回调期望req 参数的键数比post 说它可以提供的要多 是的,这也可以,但另一个答案有一个更容易实施的解决方案。 @TitianCernicova-Dragomir 我只能通过将 user 设为可选来使其工作。如果它不是可选的,编译器会抱怨该函数不能分配给类型PathParams 我目前使用此解决方案将用户注入请求以进行身份​​验证和访问控制。 (用户被注入护照中间件)。用户需要是可选的......所以它并不完美。我不喜欢扩展另一个命名空间的想法,所以......我认为这是一个偏好问题。 为什么 user 字段在 RequestWithUser 中是可选的?我注意到如果它不是可选的,这将不起作用 我同意。至少对我来说,它看起来最干净。【参考方案3】:

0

声明合并意味着编译器将两个独立的同名声明合并到一个定义中。

见下面的例子,其中接口声明被合并到打字稿中

interface Boy 
height: number;
weight: number;


interface Boy 
mark: number;


let boy: Boy = height: 6 , weight: 50, mark: 50; 

extend request response

【讨论】:

【参考方案4】:

我尝试了很多解决方案,但都没有奏效。

但它也可以如此简单并且完美运行。 当然,您可以将 AuthenticatedRequest 保存在其他任何地方。

import  IUser  from './src/_models/user.model';

export interface AuthenticatedRequest extends Request 
  user: IUser;

static async getTicket(req: AuthenticatedRequest, res: Response)

更新:

所以我从这里发现了一些有趣的东西: Import class in definition file (*d.ts).

声明命名空间 Express 应该是 typescript 的第一行,将其计为全局

declare namespace Express 

  interface Request 
    user: import('./src/modules/models/user.model').IUser;
    uploadedImage:  key: string; type: string ;
    group: import('./src/modules/models/group.model').IGroup;
  

【讨论】:

以上是关于无法在 TypeScript 中扩展 Express 请求的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript 无法在扩展语法函数调用中推断数组类型

无法扩展typescript文件

Typescript 无法导入没有扩展名的文件

无法在不提供文件扩展名的情况下导入 TypeScript 模块

在 React 和 TypeScript 中扩展 HTML 元素,同时保留 props

在 ES6 模块中使用 typescript 支持扩展全局对象,如 Autodesk.Viewing.Extension