Populate with TypeScript
Mongoose's TypeScript bindings export a PopulatedDoc
type that helps you define populated documents in your TypeScript definitions:
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();
});
Below is a simplified implementation of the PopulatedDoc
type. It takes 2 generic parameters: the populated document type PopulatedType
, and the unpopulated type RawId
.
RawId
defaults to an ObjectId.
type PopulatedDoc<PopulatedType, RawId = Types.ObjectId> = PopulatedType | RawId;
You as the developer are responsible for enforcing strong typing between populated and non-populated docs. Below is an example.
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());
}