Populate with TypeScript使用TypeScript填充

Mongoose's TypeScript bindingsMongoose的TypeScript绑定 add a generic parameter Paths to the populate():将泛型参数Paths添加到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;
});

An alternative approach is to define a PopulatedParent interface and use Pick<> to pull the properties you're populating.另一种方法是定义一个PopulatedParent接口,并使用Pick<>来提取正在填充的属性。

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;
});

Using PopulatedDoc使用PopulatedDoc

Mongoose also exports a PopulatedDoc type that helps you define populated documents in your document interface:Mongoose还导出一个PopulatedDoc类型,帮助您在文档界面中定义已填充的文档:

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();
}
});

However, we recommend using the .populate<{ child: Child }> syntax from the first section instead of PopulatedDoc. Here's two reasons why:但是,我们建议使用第一节中的.populate<{ child: Child }>语法,而不是PopulatedDoc。原因有两个:

  1. You still need to add an extra check to check if child instanceof ObjectId. 您仍然需要添加一个额外的检查来检查child instanceof ObjectIdOtherwise, the TypeScript compiler will fail with Property name does not exist on type ObjectId. 否则,TypeScript编译器将失败,因为Property name does not exist on type ObjectIdSo using PopulatedDoc<> means you need an extra check everywhere you use doc.child.因此,使用PopulatedDoc<>意味着您需要在任何使用doc.child的地方进行额外检查。
  2. In the Parent interface, child is a hydrated document, which makes it slow difficult for Mongoose to infer the type of child when you use lean() or toObject().Parent接口中,child是一个水合文档,这使得Mongoose在使用lean()toObject()时很难推断出child的类型。