使用 TypeScript 填充

Mongoose 的 TypeScript 繫結populate() 中新增了一個泛型參數 Paths

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<> 來提取您正在填充的屬性。

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

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

但是,我們建議使用第一節中的 .populate<{ child: Child }> 語法,而不是 PopulatedDoc。以下是兩個原因:

  1. 您仍然需要新增一個額外的檢查來檢查 child instanceof ObjectId。否則,TypeScript 編譯器將會失敗,並顯示 屬性名稱不存在於 ObjectId 類型。因此,使用 PopulatedDoc<> 表示您需要在每次使用 doc.child 時進行額外的檢查。
  2. Parent 介面中,child 是一個已解碼的文件,這使得 Mongoose 在您使用 lean()toObject() 時很難推斷 child 的類型。