Как расширить тип запроса Express дополнительными полями

Вопрос или проблема

Я работаю над проектом на Node.js, используя TypeScript и Express, и хочу добавить пользовательское поле (marketplaceId) к объекту Request Express через промежуточное ПО. Вот моя текущая настройка в трех пакетах:

  1. Пакет: Объявление интерфейса

    В этом пакете я объявляю интерфейс, который расширяет объект Request Express:

    import { Request } from 'express';
    
    export interface RequestWithMarketplaceId extends Request {
      marketplaceId: string;
    }
    
  2. Пакет: Реализация промежуточного ПО

    Во втором пакете я реализую промежуточное ПО, которое будет использовать расширенный интерфейс запроса:

    import { RequestWithMarketplaceId } from '@api-utils';
    import { Response, NextFunction } from 'express';
    
    // Промежуточное ПО, чтобы гарантировать, что предоставлен только один идентификатор рынка
    const ensureSingleMarketplace = (req: RequestWithMarketplaceId, res: Response, next: NextFunction) => {
      ...
    };
    
    export default ensureSingleMarketplace;
    
  3. Интеграция сервиса

    В моем сервисе я применяю промежуточное ПО, чтобы убедиться, что marketplaceId включен при обработке входящих запросов:

    import { ensureSingleMarketplace } from 'middlewares';
    import { RequestWithMarketplaceId } from 'api-utils';
    
    private intialiseRoutes(): void {
      ...
      this.router.post(`${this.path}/`, ensureSingleMarketplace, this.method);
    }
    
    private method = async (req: RequestWithMarketplaceId, res: Response, next: NextFunction) => {
    ...
    }
    

После этого я получил эту ошибку:

Нет перегрузки, соответствующей этому вызову.
  Последняя перегрузка выдала следующую ошибку.
    Аргумент типа '(req: RequestWithMarketplaceId, res: Response<any, Record<string, any>>, next: NextFunction) => Response<any, Record<string, any>> | undefined' не может быть присвоен параметру типа 'RequestHandlerParams<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
      Тип '(req: RequestWithMarketplaceId, res: Response<any, Record<string, any>>, next: NextFunction) => Response<any, Record<string, any>> | undefined' не может быть присвоен типу 'RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
        Типы параметров 'req' и 'req' несовместимы.
          Свойство 'marketplaceId' отсутствует в типе 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>', но требуется в типе 'RequestWithMarketplaceId'.ts(2769)

Я попытался создать файл express.d.ts, чтобы глобально расширить тип Request, но я не уверяюсь, правильный ли это подход или я реализовал его правильно.

Ответ или решение

Для решения проблемы расширения типа Request в Express с помощью TypeScript и добавления кастомного поля, вам необходимо следовать определённым шагам, чтобы избежать ошибок компиляции и правильно реализовать middleware. Давайте рассмотрим весь процесс более подробно.

Шаг 1: Объявление Интерфейса с Расширением

Вы уже создали интерфейс, который расширяет стандартный объект Request. Это правильный шаг. Убедитесь, что ваш интерфейс находится в файле, который будет доступен для других пакетов:

// src/types/express/index.d.ts
import { Request } from 'express';

export interface RequestWithMarketplaceId extends Request {
  marketplaceId: string; // ваше кастомное поле
}

Шаг 2: Расширение Глобальных Типов

Чтобы TypeScript распознавал ваше расширение интерфейса Request во всех частях вашего приложения, вы должны объявить его в глобальной области видимости. Создайте файл express.d.ts в корне вашего проекта или в каталоге с типами, и добавьте следующее:

// src/types/express/express.d.ts
import * as express from 'express';
import { RequestWithMarketplaceId } from './index'; // путь к вашему интерфейсу

declare global {
  namespace Express {
    interface Request {
      marketplaceId: string; // добавляем кастомное поле
    }
  }
}

Теперь ваше поле будет доступно во всем приложении, где вы используете объект Request.

Шаг 3: Реализация Middleware

Ваше middleware должно корректно использовать тип расширенного Request. Пример реализации может выглядеть следующим образом:

// src/middleware/ensureSingleMarketplace.ts
import { RequestWithMarketplaceId } from '@api-utils'; // импортируйте ваш интерфейс
import { Response, NextFunction } from 'express';

const ensureSingleMarketplace = (req: RequestWithMarketplaceId, res: Response, next: NextFunction) => {
  // Ваша логика для проверки marketplaceId
  if (!req.marketplaceId) {
    return res.status(400).json({ error: "Marketplace ID is required" });
  }
  next();
};

export default ensureSingleMarketplace;

Шаг 4: Использование Middleware в Вашем Сервисе

Теперь, когда ваше middleware готово, вы можете использовать его в вашем роутере:

// src/routes/yourRouteFile.ts
import { Router } from 'express';
import ensureSingleMarketplace from 'middlewares/ensureSingleMarketplace';
import { RequestWithMarketplaceId } from 'api-utils';

class YourService {
  private router = Router();
  private path = '/your-path';

  private initialiseRoutes(): void {
    this.router.post(`${this.path}/`, ensureSingleMarketplace, this.method);
  }

  private method = async (req: RequestWithMarketplaceId, res: Response) => {
    // Логика обработки запроса
    res.send(`Marketplace ID is: ${req.marketplaceId}`);
  };
}

Шаг 5: Убедитесь, что TypeScript Конфигурирован Правильно

Во избежание ошибок, убедитесь, что ваш tsconfig.json настроен на распознавание файлов типа *.d.ts. Вам может понадобиться добавить следующие настройки:

{
  "compilerOptions": {
    "typeRoots": ["./src/types", "./node_modules/@types"],
    "types": ["node", "express"]
  }
}

Заключение

Следуя этим шагам, вы сможете успешно расширить тип Request в Express, добавив кастомные поля и устранив ошибки компиляции. Не забывайте, что правильная организация типов и интерфейсов является ключом к стабильной и поддерживаемой кодовой базе на TypeScript. Также убедитесь, что ваши типы и интерфейсы всегда доступны в пределах вашей проектной структуры, чтобы избежать путаницы в будущем.

Оцените материал
Добавить комментарий

Капча загружается...