Skip to content

Commit

Permalink
docs(typescript): add section on PopulatedDoc to TypeScript populat…
Browse files Browse the repository at this point in the history
…e docs

Fix #11685
  • Loading branch information
vkarpov15 committed May 15, 2022
1 parent 9e215b9 commit 6b4eddc
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 2 deletions.
40 changes: 40 additions & 0 deletions docs/typescript/populate.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,43 @@ ParentModel.findOne({}).populate<Pick<PopulatedParent, 'child'>>('child').orFail
const t: string = doc.child.name;
});
```

## Using `PopulatedDoc`

Mongoose also exports a `PopulatedDoc` type that helps you define populated documents in your document interface:

```ts
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:

1. You still need to add an extra check to check if `child instanceof ObjectId`. Otherwise, the TypeScript compiler will fail with `Property name does not exist on type ObjectId`. So using `PopulatedDoc<>` means you need an extra check everywhere you use `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()`.
4 changes: 2 additions & 2 deletions test/types/populate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const childSchema: Schema = new Schema({ name: String });
const ChildModel = model<Child>('Child', childSchema);

interface Parent {
child?: PopulatedDoc<Child & Document<ObjectId>>,
child: PopulatedDoc<Document<ObjectId> & Child>,
name?: string
}

Expand All @@ -20,7 +20,7 @@ const ParentModel = model<Parent>('Parent', new Schema({
name: String
}));

ParentModel.findOne({}).populate('child').orFail().then((doc: Parent & Document<ObjectId, {}, Parent>) => {
ParentModel.findOne({}).populate('child').orFail().then((doc: Document<ObjectId, {}, Parent> & Parent) => {
const child = doc.child;
if (child == null || child instanceof ObjectId) {
throw new Error('should be populated');
Expand Down

0 comments on commit 6b4eddc

Please sign in to comment.