Búsqueda de sitios web

typescript - No se puede extender la solicitud de express


Estoy intentando extender la interfaz Request de Express como:

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');
  }
});

pero recibí el siguiente error:

Ninguna sobrecarga coincide con esta llamada. La sobrecarga 1 de 3, '(path: PathParams, ...handlers: RequestHandler[]): Router', dio el siguiente error. El argumento de tipo '(req: IRequest, res: Response) => Promise' no se puede asignar al parámetro de tipo 'RequestHandler'. Los tipos de los parámetros 'req' y 'req' son incompatibles. La propiedad 'user' falta en el tipo 'Request' pero es requerida en el tipo 'IRequest'. La sobrecarga 2 de 3, '(path: PathParams, ...handlers: RequestHandlerParams[]): Router', produjo el siguiente error. El argumento de tipo '(req: IRequest, res: Response) => Promise' no se puede asignar al parámetro de tipo 'RequestHandlerParams'. El tipo '(req: IRequest, res: Response) => Promise' no se puede asignar al tipo 'RequestHandler'.ts(2769)

Respuesta #1

Typescript no te lo permitirá, porque incluso si lo escribiste de esta manera:

router.get('/', auth, async (req: IRequest, res: Response) => {

Typescript seguirá simplemente asumiendo que express emitirá un Request y no un IRequest.

Sin embargo, dado que probablemente estás parcheando el objeto request, puedes usar la fusión de declaraciones para básicamente aumentar el objeto request interno de Express:

https://www.typescriptlang.org/docs/handbook/declaration-merging.html

Si no quieres hacer este cambio de forma global en Request, también podrías usar una aserción para asegurarte de que realmente era un 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');
}

Respuesta #2

Suponiendo que la interfaz IRequest no se esté utilizando para otros fines además de los mencionados anteriormente, se puede seguir la ruta de esta publicación en el blog de Antonello Zanini: simplemente extienda la interfaz definida por express, agregando un archivo de declaración src/types/express/index.d.ts con el siguiente contenido:

// 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;
      }
    }
  }
}

Como en la otra respuesta, el mecanismo de fusión de declaraciones de TypeScript unirá las dos declaraciones en una.

Con suerte, esta respuesta tardía será útil para algunos lectores. Yo también tuve una pregunta similar.

Artículos relacionados