我在我的Node.JS应用中使用mongoose和TypeScript。从数据库中获取数据时,我在很多地方使用mongoose的populate
。
我面临的问题是我不知道如何键入我的模型,以便属性可以是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;
}
我的架构仅将属性定义为带有ref的ObjectId。
const ItemSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: "User", index: true }
})
示例:
所以我可能会这样做:
ItemModel.findById(id).populate("user").then((item: Item) => {
console.log(item.user.name);
})
产生编译错误:
[ts] Property 'name' does not exist on type 'User | ObjectId'.
Property 'name' does not exist on type 'ObjectId'.
如何在TypeScript中使用两种类型的模型属性?
答案 0 :(得分:4)
您需要使用类型保护将类型从Types.ObjectId | User
缩小到User
...
if (item.user instanceof User) {
console.log(item.user.name);
} else {
// Otherwise, it is a Types.ObjectId
}
如果您的结构与User
匹配,但不是实例,则您需要自定义类型保护:
function isUser(obj: User | any) : obj is User {
return (obj && obj.name && typeof obj.name === 'string');
}
您可以使用以下内容:
if (isUser(item.user)) {
console.log(item.user.name);
} else {
// Otherwise, it is a Types.ObjectId
}
答案 1 :(得分:2)
Mongoose 的 TypeScript 绑定导出一个 PopulatedDoc 类型,帮助您在 TypeScript 定义中定义填充文档:
import { Schema, model, Document, PopulatedDoc } from 'mongoose';
// `child` is either an ObjectId or a populated document
interface Parent {
child?: PopulatedDoc<Child & Document>,
name?: string
}
const ParentModel = model<Parent>('Parent', new Schema({
child: { type: 'ObjectId', ref: 'Child' },
name: String
}));
interface Child {
name?: string;
}
const childSchema: Schema = new Schema({ name: String });
const ChildModel = model<Child>('Child', childSchema);
ParentModel.findOne({}).populate('child').orFail().then((doc: Parent) => {
// Works
doc.child.name.trim();
})
以下是 PopulatedDoc 类型的简化实现。它需要 2 个通用参数:填充的文档类型 PopulatedType 和未填充的类型 RawId。 RawId 默认为 ObjectId。
type PopulatedDoc<PopulatedType, RawId = Types.ObjectId> = PopulatedType | RawId;
您作为开发人员负责在填充和未填充的文档之间强制执行强类型。下面是一个例子。
ParentModel.findOne({}).populate('child').orFail().then((doc: Parent) => {
// `doc` doesn't have type information that `child` is populated
useChildDoc(doc.child);
});
// You can use a function signature to make type checking more strict.
function useChildDoc(child: Child): void {
console.log(child.name.trim());
}
这是从文档中提取的。你可以检查一下here
答案 2 :(得分:1)
您可以使用 PopulatedDoc
库中的 @types/mongoose
类型。见mongoose.doc。
答案 3 :(得分:0)
在填充用户时,将item.user
转换为User
。
ItemModel.findById(id).populate("user").then((item: Item) => {
console.log((<User>item.user).name);
})