如何结合使用mongoose populate定义模型?

max*_*paj 12 mongoose node.js typescript

背景

我在我的Node.JS应用程序中使用mongoose和TypeScript.populate从数据库中获取数据时,我在很多地方使用mongoose .

我面临的问题是我不知道如何键入我的模型,以便属性可以是ObjectId,也可以填充来自其他集合的数据.

我试过的

我试图在我的模型类型定义中使用union类型,这似乎是TypeScript提供的类似于以下内容的东西:

interface User extends Document {
    _id: Types.ObjectId;
    name: string
}

interface Item extends Document {
    _id: Types.ObjectId;

    // Union typing here
    user: Types.ObjectId | User;
}
Run Code Online (Sandbox Code Playgroud)

我的模式只将属性定义为带有ref的ObjectId.

const ItemSchema = new Schema({
    user: { type: Schema.Types.ObjectId, ref: "User", index: true }
})
Run Code Online (Sandbox Code Playgroud)

示例:

所以我可能会这样做:

ItemModel.findById(id).populate("user").then((item: Item) => {
    console.log(item.user.name);
})
Run Code Online (Sandbox Code Playgroud)

这会产生编译错误:

[ts] Property 'name' does not exist on type 'User | ObjectId'.
     Property 'name' does not exist on type 'ObjectId'.
Run Code Online (Sandbox Code Playgroud)

如何在TypeScript规则下拥有可以是基本类型或对象的模型属性?

Fen*_*ton 5

您需要使用类型防护来将类型从缩小Types.ObjectId | UserUser...

if (item.user instanceof User) {
    console.log(item.user.name);
} else {
    // Otherwise, it is a Types.ObjectId
}
Run Code Online (Sandbox Code Playgroud)

如果您的结构与匹配User,但与实例不匹配,则需要一个自定义类型防护:

function isUser(obj: User | any) : obj is User {
    return (obj && obj.name && typeof obj.name === 'string');
}
Run Code Online (Sandbox Code Playgroud)

您可以使用:

if (isUser(item.user)) {
    console.log(item.user.name);
} else {
    // Otherwise, it is a Types.ObjectId
}
Run Code Online (Sandbox Code Playgroud)