node.js - TypeScript - Ako definovať model v kombinácii s používaním mongoose populate?
Pozadie
Používam mongoose a TypeScript vo svojej Node.JS aplikácii. Používam mongoose populate na viacerých miestach pri získavaní údajov z databázy.
Problém, ktorému čelím, je, že neviem, ako typovať moje modely tak, aby vlastnosť mohla byť buď ObjectId, alebo naplnená dátami z inej kolekcie.
Čo som skúsil
Skúsil som použiť typy zjednotenia vo svojej definícii typu modelu, čo vyzerá ako niečo, čo TypeScript ponúka na riešenie takýchto vecí:
interface User extends Document {
_id: Types.ObjectId;
name: string
}
interface Item extends Document {
_id: Types.ObjectId;
// Union typing here
user: Types.ObjectId | User;
}
Môj schéma definuje vlastnosť iba ako ObjectId s odkazom.
const ItemSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: "User", index: true }
})
Príklad:
Takže by som mohol urobiť niečo takéto:
ItemModel.findById(id).populate("user").then((item: Item) => {
console.log(item.user.name);
})
Čo spôsobuje kompiláciu chyby:
[ts] Property 'name' does not exist on type 'User | ObjectId'.
Property 'name' does not exist on type 'ObjectId'.
Otázka
Ako môžem mať vlastnosť modelu, ktorá môže byť v TypeScripte jedného z dvoch typov?
Odpoveď č. 1
Musíte použiť typový strážca (type guard), aby ste zúžili typ z Types.ObjectId | User na User...
Ak pracujete s triedou User, môžete použiť toto:
if (item.user instanceof User) {
console.log(item.user.name);
} else {
// Otherwise, it is a Types.ObjectId
}
Ak máte štruktúru, ktorá zodpovedá User, ale nie je inštanciou triedy (napríklad ak je User rozhraním), budete potrebovať vlastnú ochranu typu:
function isUser(obj: User | any) : obj is User {
return (obj && obj.name && typeof obj.name === 'string');
}
Ktorý môžete použiť s:
if (isUser(item.user)) {
console.log(item.user.name);
} else {
// Otherwise, it is a Types.ObjectId
}
Ak nechcete pre tento účel kontrolovať štruktúry, môžete použiť diskriminovanú zjednotenie.
Odpoveď č. 2
Preveďte item.user na User, keď je používateľ vyplnený.
ItemModel.findById(id).populate("user").then((item: Item) => {
console.log((<User>item.user).name);
})
