-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Populate type with TypeScript not working #10758
Comments
Welcome @GameelSadek |
I was facing the same problem some time ago. The only way I found to workaround this: import mongoose, { Schema, model, Document, PopulatedDoc, Types } from 'mongoose';
async function run() {
mongoose.connect('mongodb://localhost:27017/test');
// `child` is either an ObjectId or a populated document
interface Parent {
child?: PopulatedDoc<Child>,
name?: string
}
const ParentModel = model<Parent>('Parent', new Schema<Parent>({
child: { type: Schema.Types.ObjectId, ref: 'Child' }, // Change from 'ObjectId' to `Schema.Types.ObjectId`
name: String
}));
interface Child extends Document { // Make `Child` extends `Document`
_id: Types.ObjectId, // Explicitly define `_id` type
name?: string;
}
const childSchema: Schema = new Schema<Child>({ name: String });
const ChildModel = model<Child>('Child', childSchema);
const parent = await ParentModel.findOne({}).populate('child')
parent!.child.name;
} Although Mongoose does not recommend extending |
Note that even with the const parent = await ParentModel.findOne({}).populate('child').orFail();
(parent.child as Child | null)?.name; In order to access the name property of child without errors. PS: I asserted |
Just found another workaround without extending Document: import mongoose, { Schema, model, Document, PopulatedDoc, Types } from 'mongoose';
async function run() {
mongoose.connect('mongodb://localhost:27017/test');
// `child` is either an ObjectId or a populated document
interface Parent {
child?: PopulatedDoc<Child & Document<Types.ObjectId>>, // Pass `_id` type to `Document` generic parameter
name?: string
}
const ParentModel = model<Parent>('Parent', new Schema<Parent>({
child: { type: Schema.Types.ObjectId, ref: 'Child' }, // Change from 'ObjectId' to `Schema.Types.ObjectId`
name: String
}));
interface Child {
name?: string;
}
const childSchema: Schema = new Schema<Child>({ name: String });
const ChildModel = model<Child>('Child', childSchema);
const parent = await ParentModel.findOne({}).populate('child')
parent!.child.name;
} |
@thiagokisaki using your example above, there's still no |
property |
Lines 1687 to 1690 in bf4f107
|
I was leaving a note for valeri karpov when he reads the thread |
Sorry, actually my previous comment was a reply to @ahmedelshenawy25 comment. |
@thiagokisaki we made some improvements to make this easier by passing a generic param to 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;
}
// `PopulatedParent` represents the possible populated paths
interface PopulatedParent {
child: Child | null;
}
const ParentModel = model<Parent>('Parent', new Schema({
child: { type: '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;
}); |
Ok @vkarpov15 It seems that the generic populate method returns an intersection type. Example: |
@judgegodwins can you please elaborate? |
@vkarpov15 I'm running in the same issue as @judgegodwins regarding the populate method returning an intersection type: ParentModel.findOne({})
.populate<{ child: Child }>('child')
.orFail()
.then((doc) => {
const t: string = doc.child.name
}) The type of Even when using Typescript 4.2.4 / Mongoose 6.0.13 |
@SherloxTV can you please provide a more complete repro script? The below script compiles correctly with TypeScript 4.2.4: import { Schema, model, Document, PopulatedDoc, Types } from 'mongoose';
async function run() {
// `child` is either an ObjectId or a populated document
interface Parent {
child?: Types.ObjectId,
name?: string
}
const ParentModel = model<Parent>('Parent', new Schema<Parent>({
child: { type: 'ObjectId', ref: 'Child' },
name: String
}));
interface Child {
name?: string;
}
const childSchema: Schema = new Schema<Child>({ name: String });
const ChildModel = model<Child>('Child', childSchema);
ParentModel.findOne({}).populate<{ child: Child }>('child').orFail().then(doc => {
const t: string = doc.child.name;
});
} |
Hi, this isn't about any compilation issues, it's just the actual behavior of the
Am I misunderstanding how this should work or is there something not making sense to you as well ? |
I'm confused by what you mean by "the actual behavior of the populate regarding types"? |
@vkarpov15 's code seems to work but i'm having issues with
|
@dantenol please open a new issue and follow the issue template |
Do you want to request a feature or report a bug?
bug
What is the current behavior?
property in interface with type
PopulatedDoc<interface & Document>
has type anyIf the current behavior is a bug, please provide the steps to reproduce.
What is the expected behavior?
it should support the type of given interface
What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
Node.js: 14.17.4 , Mongoose: 6.0.7 , MongoDB: 4.4.3
The text was updated successfully, but these errors were encountered: