mongodb - Cómo tipar correctamente un array de IDs de objetos en mongoose con Typescript
Tengo un modelo de publicación:
const PostSchema = new Schema<IPost>(
{
// ...
likes: [{ type: Schema.Types.ObjectId, ref: "User" }],
// ...
}
)
export default model<IPost>("Post", PostSchema)
export interface IPost {
// ...
likes: ObjectId[]
// ...
}
export interface IPostDocument extends Document, IPost {}
Y estoy intentando alternar un usuario como:
export const toggleLike: TController = async (req, res, next) => {
const user = req.user as IUserDocument;
const userId = user._id;
const postId = req.params.postId;
try {
const disliked = await PostModel.findOneAndUpdate(
{ _id: postId, likes: userId },
{ $pull: { likes: userId } }
); // works with no problem
if (disliked)
res.json({ message: `User ${userId} disliked post ${postId}` });
else {
const liked = await PostModel.findOneAndUpdate(
{ _id: postId },
{ $push: { likes: userId } }
); // the $push throws an error "Type instantiation is excessively deep and possibly infinite."
if (liked) res.json({ message: `User ${userId} liked post ${postId}` });
else return next(createError(404, "Post not found"));
}
} catch (error) {
next(createError(500, error as Error));
}
};
El operador $push de Mongo está generando un error "La instanciación de tipo es excesivamente profunda y posiblemente infinita."
Dudo que ayude, pero la descripción del error es:
(propiedad) likes?: _AllowStringsForIds<(((((((((((... | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[]> | ArrayOperator<(_AllowStringsForIds<(((((((((((... | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[]> | undefined)[]> | undefined
¿Alguna idea de lo que está pasando?
Respuesta #1
Supongo que tu pregunta es muy relevante para esta mía: Nestjs: Esquema correcto para un array de subdocumentos en mongoose (sin _id por defecto o redefinir ObjectId). Excepto que no quiero tener ObjectID pero tú sí.
El problema es que MongooseDocument<Array> y el Array de TS son bastante iguales, pero tienen métodos diferentes.
Estoy trabajando con Nestjs, así que proporcionaré un ejemplo para ello, pero supongo que encontraste mi ejemplo muy útil.
Por ejemplo, para un arreglo de documentos incrustados necesitas tener la siguiente interfaz de esquema:
En el esquema infantil:
@Schema()
class Field extends Document {
@Prop({ type: MongooseSchema.Types.ObjectId })
_id: MongooseSchema.Types.ObjectId
@Prop({ type: String })
name: string;
}
export const FieldSchema = SchemaFactory.createForClass(Field);
En el esquema principal:
@Prop({ _id: true, type: [FieldSchema] })
field: MongooseSchema.Types.Array<Field>;
Y la parte más importante son las importaciones:
import { Document, Schema as MongooseSchema, Types } from "mongoose";
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
Por supuesto, en el proyecto TS por defecto, sin Nest.js no tienes nest/mongoose, pero como ya puedes entender, todo es relevante con las interfaces y sus tipos que estamos tomando de mongoose mismo.
Entonces class es una interfaz Schema, que se extiende de la interfaz Document de mongoose y todos los arreglos no son solo arreglos simples de TS, sino Schema.Types.Array (de mongoose). También puedes usar no solo ObjectId, sino otros tipos especiales solo de Mongo, como números Decimal128 y Mixed, etc., según la documentación.
Entonces, en tu caso con Arrays de ObjectIds, solo necesitas usar:
MongooseSchema.Types.Array<MongooseSchema.Types.ObjectId>
o de una manera un poco más primitiva:
En Nest en el decorador @Prop proporcionas el tipo para el tipo de campo de MongoDB, por lo que es lo mismo que en TS por defecto:
new Schema({ languages: { type: ObjectId} })
Así que espero que te ayude en tu proyecto.
Respuesta #2
Importar ObjectId de mongoose en la declaración de mi interfaz resolvió el problema. Antes de eso, lo estaba usando pero sin importarlo de ningún lugar (ni siquiera sabía qué era eso).

