Automattic / mongoose

MongoDB object modeling designed to work in an asynchronous environment.
https://mongoosejs.com
MIT License
26.97k stars 3.84k forks source link

Lean query results lose populated types #11532

Closed magyarb closed 2 years ago

magyarb commented 2 years ago

Do you want to request a feature or report a bug? Bug

What is the current behavior? Lean query results lose populated types.

If the current behavior is a bug, please provide the steps to reproduce. Live demo: https://stackblitz.com/edit/ts-node-8yfhy1?file=index.ts

import { model, Schema, Types } from 'mongoose';

interface IObject {
  _id: Types.ObjectId;
  createdAt: Date;
  updatedAt: Date;
}

interface IParent extends IObject {
  name: string;
  child: Types.ObjectId;
}

const ParentObj: Record<keyof Omit<IParent, keyof IObject>, any> = {
  name: { type: String, required: true },
  child: { type: Schema.Types.ObjectId, ref: 'Child', required: true },
};

const parentSchema = new Schema(ParentObj, {
  timestamps: true,
  autoCreate: true,
});

const parent = model<IParent>('Parent', parentSchema);

interface IChild extends IObject {
  name: string;
}

const ChildObj: Record<keyof Omit<IChild, keyof IObject>, any> = {
  name: { type: String, required: true },
};

const childSchema = new Schema(ChildObj, {
  timestamps: true,
  autoCreate: true,
});

const child = model<IChild>('Child', childSchema);

async function main() {
  const result = await parent.findOne().populate<{ child: IChild }>('child');
  console.log(result.child.name);
  //this works

  const leanResult = await parent
    .findOne()
    .populate<{ child: IChild }>('child')
    .lean();
  console.log(leanResult.child.name);
  // throws error: Property 'name' does not exist on type 'ObjectId'.(2339)
}

main();

What is the expected behavior? The lean document's type should be IParent & {child: IChild} or something similar, to allow access to the child's properties.

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version. node v14.19.0 mongoose v6.2.6 mongodb v4.3.1

maikknebel commented 2 years ago

I got the same problem with the upgrade from 6.2.5 to 6.2.6. The pull-request and the comments linked are related to this, i think

11503

https://github.com/Automattic/mongoose/issues/11518#issuecomment-1066996910

magyarb commented 2 years ago

Downgrading to v6.2.5 does not solve the problem. Additionally, it breaks the first example too.

async function main() {
  const result = await parent.findOne().populate<{ child: IChild }>('child');
  console.log(result.child.name);
  /**
    Property 'child' does not exist on type '(Omit<unknown, "child"> & { child: IChild; })[] | (Omit<any, "child"> & { child: IChild; })'.
    Property 'child' does not exist on type '(Omit<unknown, "child"> & { child: IChild; })[]'.(2339)
   */

  const leanResult = await parent
    .findOne()
    .populate<{ child: IChild }>('child')
    .lean();
  console.log(leanResult.child.name);
  /**
    Property 'child' does not exist on type '(Omit<any, "child"> & { child: IChild; }) | (IParent & { _id: ObjectId; })[]'.
    Property 'child' does not exist on type '(IParent & { _id: ObjectId; })[]'.(2339)
   */
}

Live demo: https://stackblitz.com/edit/ts-node-fmtkmp?file=index.ts

Uzlopak commented 2 years ago

Should be fixed now. If the bug still exists after you updated mongoose, then feel free to reopen an issue.