TypeScript: ¿Cómo definir el modelo en combinación con el uso de población de mangosta?
Fondo
Estoy usando mangosta y TypeScript en mi aplicación Node.JS. Estoy usando mangosta populate
en varios lugares cuando obtengo datos de la base de datos.
El problema al que me enfrento es que no sé cómo escribir mis modelos para que una propiedad pueda ser un ObjectId o completarse con datos de otra colección.
lo que he probado
Intenté usar tipos de unión en la definición de tipo de mi modelo, que parece algo que TypeScript ofrece para cubrir este tipo de cosas:
interface User extends Document {
_id: Types.ObjectId;
name: string
}
interface Item extends Document {
_id: Types.ObjectId;
// Union typing here
user: Types.ObjectId | User;
}
Mi esquema solo define la propiedad como un ObjectId con ref.
const ItemSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: "User", index: true }
})
Ejemplo :
Entonces podría hacer algo como esto:
ItemModel.findById(id).populate("user").then((item: Item) => {
console.log(item.user.name);
})
Lo que produce el error de compilación:
[ts] Property 'name' does not exist on type 'User | ObjectId'.
Property 'name' does not exist on type 'ObjectId'.
Pregunta
¿Cómo puedo tener una propiedad de modelo que pueda ser de dos tipos en TypeScript?
Necesitas usar una protección tipográfica para limitar el tipo de letra Types.ObjectId | User
a User
...
Si se trata de una User
clase, puede utilizar esto:
if (item.user instanceof User) {
console.log(item.user.name);
} else {
// Otherwise, it is a Types.ObjectId
}
Si tiene una estructura que coincide con una User
clase, pero no con una instancia (por ejemplo, si User
es una interfaz), necesitará una protección de tipo personalizada:
function isUser(obj: User | any) : obj is User {
return (obj && obj.name && typeof obj.name === 'string');
}
Que puedes usar con:
if (isUser(item.user)) {
console.log(item.user.name);
} else {
// Otherwise, it is a Types.ObjectId
}
Si no desea verificar las estructuras para este propósito, puede utilizar una unión discriminada .
Transmita el item.user
destino a User
, cuando el usuario esté completo.
ItemModel.findById(id).populate("user").then((item: Item) => {
console.log((<User>item.user).name);
})
Puedes usar PopulatedDoc
el tipo de la @types/mongoose
lib. Ver mangosta.doc .