Как расширить объект Request в Express с помощью TypeScript
Правильный способ расширения Request через Declaration Merging
В Express (и других библиотеках на TypeScript) используется механизм Declaration Merging (слияния объявлений). Это позволяет расширить существующий интерфейс, добавив в него новые свойства. Для Express нужно расширить интерфейс Request из пакета express.
Вот пошаговое решение:
Шаг 1: Создайте файл объявлений типов
Создайте файл types/express.d.ts (или @types/express/index.d.ts) в корне проекта:
// types/express.d.ts
import { Request } from 'express';
// Расширяем глобальный интерфейс Express Request
declare global {
namespace Express {
interface Request {
user?: {
id: string;
email: string;
role: 'admin' | 'user';
};
}
}
}
Шаг 2: Настройте tsconfig.json
Убедитесь, что TypeScript видит ваши декларации. Добавьте в tsconfig.json:
{
"compilerOptions": {
// ... другие настройки
"typeRoots": ["./node_modules/@types", "./types"]
},
"include": ["src/**/*", "types/**/*"]
}
Шаг 3: Используйте в middleware
Теперь TypeScript знает о свойстве user:
// middleware/auth.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
export const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'Нет токена' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as Express.Request['user'];
req.user = decoded;
next();
} catch (error) {
return res.status(403).json({ message: 'Неверный токен' });
}
};
Шаг 4: Используйте в контроллерах
Свойство доступно без дополнительных импортов:
// controllers/userController.ts
import { Request, Response } from 'express';
export const getProfile = (req: Request, res: Response) => {
// req.user — доступен и типизирован!
if (!req.user) {
return res.status(401).json({ message: 'Не авторизован' });
}
res.json({
id: req.user.id,
email: req.user.email,
role: req.user.role
});
};
Важные моменты
- Обязательно используйте
declare global— это добавляет типы в глобальную область видимости Express. - Сделайте свойство опциональным (
user?) — так TypeScript не будет ругаться, если вы не установили его в middleware. - Не импортируйте ничего в файле .d.ts — если нужно, используйте синтаксис
import()внутри объявления. - Для сложных типов вынесите их в отдельный интерфейс и используйте перекрестное объявление (intersection).
Альтернатива: модульное расширение
Если не хотите глобальных типов, можно использовать модульный подход:
// types/express.d.ts
import { Request } from 'express';
export type AuthenticatedRequest = Request & {
user: {
id: string;
email: string;
role: 'admin' | 'user';
};
};
Но тогда придётся явно указывать тип AuthenticatedRequest в каждом контроллере, что менее удобно.
Глобальное расширение через Declaration Merging — это стандартный и рекомендуемый способ. Он используется в официальной документации Express и в большинстве реальных проектов.
<<Альтернативный подход: использование модуля express-serve-static-core
Некоторые разработчики предпочитают расширять интерфейс через модуль express-serve-static-core, который является внутренней зависимостью Express. Это может быть полезно, если вы хотите более явно контролировать расширение:
// types/express.d.ts
import 'express-serve-static-core';
declare module 'express-serve-static-core' {
interface Request {
user?: {
id: string;
email: string;
role: 'admin' | 'user';
};
}
}
Плюсы этого подхода:
- Не нужно использовать
declare global— расширение происходит на уровне модуля - Меньше шансов случайно задеть другие глобальные типы
- Работает одинаково хорошо с последними версиями Express и TypeScript
Минусы:
- Требует установки
@types/express-serve-static-core(обычно идёт в комплекте с@types/express) - Может быть менее очевидным для новых разработчиков
Оба подхода валидны и работают. Выбирайте тот, который кажется вам чище и понятнее для вашей команды. Главное — не забудьте настроить typeRoots в tsconfig.json, если ваш файл деклараций лежит не в стандартной папке.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Полный разбор модуля Math в Python: математические функции и примеры применения