Skip to content
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

Documentation about using populate() with TypeScript #10212

Closed
z164 opened this issue May 6, 2021 · 2 comments
Closed

Documentation about using populate() with TypeScript #10212

z164 opened this issue May 6, 2021 · 2 comments
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation typescript Types or Types-test related issue / Pull Request
Milestone

Comments

@z164
Copy link

z164 commented May 6, 2021

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

What is the current behavior?
Populating entity in any form (Model / Document) does not return actual type of populated doc in, instead it returns non-populated document type. It causes many type bindings like (as unknown as Actual type) and //@ts-ignores. Want to see a beam of light that it can be resolved in other way, than specified.

If the current behavior is a bug, please provide the steps to reproduce.
Literally any entity.populate()

What is the expected behavior?
Expected behavior is resolving "Not assignable types (Actual populated entity with nested object to Types.ObjectId)" TypeScript Error

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
Node v16, MongoDB v4.4.5, Mongoose v5.12.7

@IslandRhythms IslandRhythms added the needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue label May 6, 2021
@z164
Copy link
Author

z164 commented May 7, 2021

import * as mongoose from "mongoose";
import { Types } from "mongoose";

const SingleItemSchema = new mongoose.Schema({
  title: String,
  description: String,
});

const GroupOfItemsSchema = new mongoose.Schema({
  title: String,
  items: { type: [Types.ObjectId], ref: "SingleItem" },
});

type SingleDocument = {
  title: string;
  description: string;
} & mongoose.Document;

type GroupDocument = {
  title: string;
  items: [Types.ObjectId];
} & mongoose.Document;

interface IPopulatedGroup extends Omit<GroupDocument, "items"> {
  items: [
    {
      title: string;
      description: string;
    }
  ];
}

const singleModel = mongoose.model<SingleDocument>(
  "SingleItem",
  SingleItemSchema
);
const groupModel = mongoose.model<GroupDocument>(
  "GroupOfItems",
  GroupOfItemsSchema
);

async function main() {
  await mongoose.connect("mongodb://localhost:27017/issue");
  const single = await singleModel.create({
    title: "1",
    description: "First element",
  });
  const group = await groupModel.create({
    title: "Group of items",
    items: [single._id],
  });

  async function populateGroupDoc(
    group: GroupDocument
  ): Promise<IPopulatedGroup> {
    // need conversion to ANY on populated paths, like 'axios' does with his unknown fields
    /*{
	"resource": "/Projects/issue/index.ts",
	"owner": "typescript",
	"code": "2322",
	"severity": 8,
	"message": "Type 'GroupDocument' is not assignable to type 'IPopulatedGroup'.
    Types of property 'items' are incompatible.
    Type '[ObjectId]' is not assignable to type '[{ title: string; description: string; }]'.
    Type 'ObjectId' is missing the following properties from type '{ title: string; description: string; }': title, description",
	"source": "ts",
	"startLineNumber": 47,
	"startColumn": 7,
	"endLineNumber": 47,
	"endColumn": 52
}*/
    return group.populate("items").execPopulate();
  }

  async function populateGroupModel(
    groupModel: mongoose.Model<GroupDocument>,
    id: string
  ): Promise<IPopulatedGroup> {
    return groupModel.findById(id).populate("items");
  }

  async function populateGroupV2(group: GroupDocument): Promise<GroupDocument> {
    // has no ts-error
    return group.populate("items").execPopulate();
  }

  console.log(await populateGroupDoc(group));
  console.log(await populateGroupModel(groupModel, group._id));
  const populatedGroup = await populateGroupV2(group);
  console.log(populatedGroup);
  // Each populates Correctly

  populatedGroup.items.forEach((item) => {
    // Expects that item is stil Object.Id
    // Property 'title' does not exist on type 'ObjectId'.
    console.log(item.title);
  });

  mongoose.disconnect();
}

main();

@IslandRhythms IslandRhythms added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. typescript Types or Types-test related issue / Pull Request and removed needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue labels May 10, 2021
@vkarpov15 vkarpov15 added this to the 5.12.9 milestone May 10, 2021
@vkarpov15
Copy link
Collaborator

We recommend using Mongoose's PopulatedDoc type for this, it's a union type that says the given path can be an id or a populated doc. See below example:

import * as mongoose from "mongoose";
import { Types, PopulatedDoc } from "mongoose";

const SingleItemSchema = new mongoose.Schema({
  title: String,
  description: String,
});

const GroupOfItemsSchema = new mongoose.Schema({
  title: String,
  items: { type: [Types.ObjectId], ref: "SingleItem" },
});

type SingleDocument = {
  title: string;
  description: string;
} & mongoose.Document;

type GroupDocument = {
  title: string;
  items: PopulatedDoc<SingleDocument>[];
} & mongoose.Document;

const singleModel = mongoose.model<SingleDocument>(
  "SingleItem",
  SingleItemSchema
);
const groupModel = mongoose.model<GroupDocument>(
  "GroupOfItems",
  GroupOfItemsSchema
);

interface IPopulatedGroup extends Omit<GroupDocument, "items"> {
  items: SingleDocument[];
}

async function main() {
  await mongoose.connect("mongodb://localhost:27017/issue");
  const single = await singleModel.create({
    title: "1",
    description: "First element",
  });
  const group = await groupModel.create({
    title: "Group of items",
    items: [single._id],
  });

  async function populateGroupDoc(
    group: GroupDocument
  ): Promise<IPopulatedGroup> {
        return group.populate("items").execPopulate();
  }

  async function populateGroupModel(
    groupModel: mongoose.Model<GroupDocument>,
    id: string
  ): Promise<IPopulatedGroup> {
    return groupModel.findById(id).populate("items");
  }

  async function populateGroupV2(group: GroupDocument): Promise<GroupDocument> {
    // has no ts-error
    return group.populate("items").execPopulate();
  }

  console.log(await populateGroupDoc(group));
  console.log(await populateGroupModel(groupModel, group._id));
  const populatedGroup = await populateGroupV2(group);
  console.log(populatedGroup);
  // Each populates Correctly

  populatedGroup.items.forEach((item) => {
    console.log(item.title);
  });

  mongoose.disconnect();
}

main();

We should add a note to our docs about this.

@vkarpov15 vkarpov15 added docs This issue is due to a mistake or omission in the mongoosejs.com documentation and removed confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels May 11, 2021
@vkarpov15 vkarpov15 changed the title Populated entity type conflicts Documentation about using populate() with TypeScript May 11, 2021
@vkarpov15 vkarpov15 modified the milestones: 5.12.9, 5.12.10 May 13, 2021
3jins added a commit to 3jins/blog-renewal that referenced this issue Sep 20, 2021
- Fixed Mongoose Schema (Replace inheritance structure to intersection)
  Refer this: Automattic/mongoose#10189 (comment)
  and this: Automattic/mongoose#10212 (comment)
3jins added a commit to 3jins/blog-renewal that referenced this issue Nov 21, 2021
- Fixed Mongoose Schema (Replace inheritance structure to intersection)
  Refer this: Automattic/mongoose#10189 (comment)
  and this: Automattic/mongoose#10212 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation typescript Types or Types-test related issue / Pull Request
Projects
None yet
Development

No branches or pull requests

3 participants