使用 TypeScript 填充
¥Populate with TypeScript
Mongoose 的 TypeScript 绑定 向 populate()
添加通用参数 Paths
:
¥Mongoose's TypeScript bindings add a generic parameter Paths
to the populate()
:
import { Schema, model, Document, Types } from 'mongoose';
// `Parent` represents the object as it is stored in MongoDB
interface Parent {
child?: Types.ObjectId,
name?: string
}
const ParentModel = model<Parent>('Parent', new Schema({
child: { type: Schema.Types.ObjectId, ref: 'Child' },
name: String
}));
interface Child {
name: string;
}
const childSchema: Schema = new Schema({ name: String });
const ChildModel = model<Child>('Child', childSchema);
// Populate with `Paths` generic `{ child: Child }` to override `child` path
ParentModel.findOne({}).populate<{ child: Child }>('child').orFail().then(doc => {
// Works
const t: string = doc.child.name;
});
另一种方法是定义 PopulatedParent
接口并使用 Pick<>
来提取你正在填充的属性。
¥An alternative approach is to define a PopulatedParent
interface and use Pick<>
to pull the properties you're populating.
import { Schema, model, Document, Types } from 'mongoose';
// `Parent` represents the object as it is stored in MongoDB
interface Parent {
child?: Types.ObjectId,
name?: string
}
interface Child {
name: string;
}
interface PopulatedParent {
child: Child | null;
}
const ParentModel = model<Parent>('Parent', new Schema({
child: { type: Schema.Types.ObjectId, ref: 'Child' },
name: String
}));
const childSchema: Schema = new Schema({ name: String });
const ChildModel = model<Child>('Child', childSchema);
// Populate with `Paths` generic `{ child: Child }` to override `child` path
ParentModel.findOne({}).populate<Pick<PopulatedParent, 'child'>>('child').orFail().then(doc => {
// Works
const t: string = doc.child.name;
});
使用 PopulatedDoc
¥Using PopulatedDoc
Mongoose 还导出 PopulatedDoc
类型,帮助你在文档界面中定义填充文档:
¥Mongoose also exports a PopulatedDoc
type that helps you define populated documents in your document interface:
import { Schema, model, Document, PopulatedDoc } from 'mongoose';
// `child` is either an ObjectId or a populated document
interface Parent {
child?: PopulatedDoc<Document<ObjectId> & Child>,
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) => {
const child = doc.child;
if (child == null || child instanceof ObjectId) {
throw new Error('should be populated');
} else {
// Works
doc.child.name.trim();
}
});
但是,我们建议使用第一部分中的 .populate<{ child: Child }>
语法而不是 PopulatedDoc
。原因有两个:
¥However, we recommend using the .populate<{ child: Child }>
syntax from the first section instead of PopulatedDoc
.
Here's two reasons why:
你仍然需要添加额外的检查来检查
child instanceof ObjectId
。否则,TypeScript 编译器将失败并显示Property name does not exist on type ObjectId
。因此,使用PopulatedDoc<>
意味着你在使用doc.child
的任何地方都需要进行额外检查。¥You still need to add an extra check to check if
child instanceof ObjectId
. Otherwise, the TypeScript compiler will fail withProperty name does not exist on type ObjectId
. So usingPopulatedDoc<>
means you need an extra check everywhere you usedoc.child
.在
Parent
接口中,child
是一个水合文档,这使得当你使用lean()
或toObject()
时,Mongoose 很难推断出child
的类型。¥In the
Parent
interface,child
is a hydrated document, which makes it slow difficult for Mongoose to infer the type ofchild
when you uselean()
ortoObject()
.