Вопрос или проблема
Я работаю над проектом на Node.js, используя TypeScript и Express, и хочу добавить пользовательское поле (marketplaceId
) к объекту Request
Express через промежуточное ПО. Вот моя текущая настройка в трех пакетах:
-
Пакет: Объявление интерфейса
В этом пакете я объявляю интерфейс, который расширяет объект Request Express:
import { Request } from 'express'; export interface RequestWithMarketplaceId extends Request { marketplaceId: string; }
-
Пакет: Реализация промежуточного ПО
Во втором пакете я реализую промежуточное ПО, которое будет использовать расширенный интерфейс запроса:
import { RequestWithMarketplaceId } from '@api-utils'; import { Response, NextFunction } from 'express'; // Промежуточное ПО, чтобы гарантировать, что предоставлен только один идентификатор рынка const ensureSingleMarketplace = (req: RequestWithMarketplaceId, res: Response, next: NextFunction) => { ... }; export default ensureSingleMarketplace;
-
Интеграция сервиса
В моем сервисе я применяю промежуточное ПО, чтобы убедиться, что
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. Также убедитесь, что ваши типы и интерфейсы всегда доступны в пределах вашей проектной структуры, чтобы избежать путаницы в будущем.