Búsqueda de sitios web

node.js - La propiedad 'id' no existe en el tipo 'User' con Passport y Typescript


Usando PassportJS en Express con Typescript, obtengo el siguiente error al intentar acceder a req.user.id.

Property 'id' does not exist on type 'User'.ts(2339)

Al registrar solo req.user en la consola, puedo ver claramente que hay una propiedad id, sin embargo, Typescript sigue lanzando este error.

Aquí hay un fragmento del código que estoy intentando ejecutar,

userRouter.post("/session", async (req: Request, res: Response) => {
  const { startTime, endTime, desc } = req.body;
  const { user } = req;

  console.log(user!.id);

  res.json({ message: "CREATE NEW SESSION" });
});

Como soy nuevo en TS, no estoy seguro de los pasos a seguir para resolver este problema. Supongo que tengo que cambiar una interfaz o algo en la configuración en algún lugar.

Respuesta #1

PassportJS no declara ninguna propiedad de la interfaz User. Puedes ampliar esta definición fusionándola con lo siguiente en tu código:

declare global {
  namespace Express {
    interface User {
      id: string
    }
  }
}

y tu código debería compilar.

¿Pero dónde esperas que se llene el user.id? ¿Has escrito tu propio código para eso? ¿O PassportJS llena esta propiedad aunque no la declare?

(Pude ">engañar" al compilador de Typescript reemplazando user!.id por user["id"], quizás porque no usé la opción --strict.)

Respuesta #2

Me encontré con el mismo error y lo resolví de la siguiente manera:

Hice la interfaz IUser:

import type { Document } from "mongoose";

interface IUser extends Document {
  name: string;
  email: string;
  hash: string;
  salt: string;
  isAdmin: boolean;
}

export default IUser;

Y lo uso en el modelo de usuario:

import { Schema, model } from "mongoose";
import IUser from "../types/User";

const userSchema = new Schema<IUser>(
  {
    name: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
      unique: true,
    },
    hash: {
      type: String,
      required: true,
    },
    salt: {
      type: String,
      required: true,
    },
    isAdmin: {
      type: Boolean,
      required: true,
      default: false,
    },
  },
  {
    timestamps: true,
  }
);

const User = model("User", userSchema);

export default User;

Y, en el controlador de usuarios, lo usé así:

import type { Request, Response } from "express";
import { genPassword } from "../utils/passwordUtils";
import User from "../models/userModel";
import IUser from "../types/User";

export const getUserProfile = async (req: Request, res: Response) => {
  const currentUser = req.user as IUser;
  try {
    const user = await User.findById(currentUser._id);

    if (user) {
      res.json({ success: true, user });
    } else {
      res.status(404);
      throw new Error("User not found");
    }
  } catch (error) {
    res.status(404);
    throw new Error("User not found");
  }
};

export const updateUserProfile = async (req: Request, res: Response) => {
  const currentUser = req.user as IUser;
  const user = await User.findById(currentUser._id);

  if (user) {
    user.name = req.body.name || user.name;
    user.email = req.body.email || user.email;
    if (req.body.password) {
      const saltHash = genPassword(req.body.password);
      const salt = saltHash.salt;
      const hash = saltHash.hash;
      user.hash = hash;
      user.salt = salt;
    }
    const updatedUser = await user.save();
    res.json({ success: true, user: updatedUser });
  } else {
    res.status(404);
    throw new Error("User not found");
  }
};