#typescript #express
#typescript #экспресс
Вопрос:
Я пытаюсь расширить Request
интерфейс Express
как:
import express, { Request, Response } from 'express';
interface IRequest extends Request {
user: {
id: string;
}
}
const router = express.Router();
router.get('/', auth, async (req: IRequest, res: Response) => {
try {
const user = await User.findById(req.user.id).select('-password');
res.json(user);
} catch (e) {
console.error((e as Error).message);
res.status(500).send('Server Error');
}
});
но я получил следующую ошибку:
Никакая перегрузка не соответствует этому вызову. Перегрузка 1 из 3, ‘(путь: параметры пути, … обработчики: RequestHandler<ParamsDictionary, any, any, ParsedQs>[]): Router’, выдала следующую ошибку. Аргумент типа ‘(запрос: IRequest, res: Response) => Обещание’ не может быть присвоен параметру типа ‘RequestHandler<ParamsDictionary, any, any, ParsedQs>’. Типы параметров ‘req’ и ‘req’ несовместимы. Свойство ‘user’ отсутствует в типе ‘Request<ParamsDictionary, any, any, ParsedQs>’, но требуется в типе ‘IRequest’. Перегрузка 2 из 3, ‘(путь: параметры пути, … обработчики: RequestHandlerParams<ParamsDictionary, any, any, ParsedQs>[]): Router’, выдала следующую ошибку. Аргумент типа ‘(запрос: IRequest, res: Response) => Обещание’ не может быть присвоен параметру типа ‘RequestHandlerParams<ParamsDictionary, any, any, ParsedQs>’. Тип ‘(запрос: IRequest, res: Response) => Обещание’ не может быть присвоен типу ‘RequestHandler<ParamsDictionary, any, any, ParsedQs>’.ts(2769)
Ответ №1:
Typescript не позволит вам, потому что, даже если вы ввели его таким образом:
router.get('/', auth, async (req: IRequest, res: Response) => {
Typescript все равно будет просто предполагать, что express выдаст Request
, а не IRequest
.
Однако, поскольку вы, вероятно, исправляете объект запроса вручную, вы можете использовать слияние объявлений, чтобы в основном увеличить объект экспресс-внутреннего запроса:
https://www.typescriptlang.org/docs/handbook/declaration-merging.html
Если вы не хотите глобально вносить это изменение в Request
, вы также можете использовать утверждение, чтобы убедиться, что это действительно было IRequest
:
router.get('/', auth, async (req: request, res: Response) => {
try {
assertIRequest(req);
const user = await User.findById(req.user.id).select('-password');
res.json(user);
} catch (e) {
console.error((e as Error).message);
res.status(500).send('Server Error');
}
});
function assertIRequest(req: Request|IRequest): asserts req is IRequest {
if (!req?.user?.id) throw new Error('Request was not an IRequest');
}
Комментарии:
1. Спасибо за ответ! Значит, нет способа просто указать тип как
IRequest
?2. Да, нет никакого способа.
3. Прежде чем читать этот ответ, я пошел по пути создания общего для
Request
, но это создало другие конфликты при попытке использовать другое промежуточное программное обеспечение. Могу ли я предположить из этого ответа, что обычно лучше просто оставитьRequest
в покое (за исключением объявления модуля для добавления объектов вreq
)?
Ответ №2:
Предполагая, что интерфейс IRequest
не используется для других целей, помимо вышеуказанных, можно следовать маршруту, описанному в этом сообщении в блоге Антонелло Занини: просто расширьте интерфейс, определенный express, добавив файл объявления src/types/express/index.d.ts
со следующим содержимым:
// src/types/express/index.d.ts
// to make the file a module and avoid the TypeScript error
export {}
declare global {
namespace Express {
export interface Request {
user: {
id: string;
}
}
}
}
Как и в другом ответе, механизм слияния объявлений TypeScript объединит два объявления в одно.
Надеюсь, этот поздний ответ будет полезен некоторым читателям. У меня только что возник аналогичный вопрос.