Невозможно расширить экспресс-запрос

#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 объединит два объявления в одно.

Надеюсь, этот поздний ответ будет полезен некоторым читателям. У меня только что возник аналогичный вопрос.