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

How to write a generic function to resolve PopulatedDoc type variables? #15121

Open
1 task done
nikzanda opened this issue Dec 19, 2024 · 1 comment
Open
1 task done
Labels
help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary

Comments

@nikzanda
Copy link

Prerequisites

  • I have written a descriptive issue title

Mongoose version

8.9.1

Node.js version

20.10.0

MongoDB version

6.0.2

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

No response

Issue

In my project, I often find myself having to implement this code:

// Check if 'myPopulatedDocProp' has already been populated in a previous part of the code
if (myDocument.myPopulatedDocProp instanceof AnotherModel) {
  return myDocument.myPopulatedDocProp;
}

// If 'myPopulatedDocProp' was not populated, perform a database query to find and populate the document property
const result = await AnotherModel
  .findById(myDocument.myPopulatedDocProp) // Query the AnotherModel to find the document by ID
  .orFail(); // Throw an error if the document is not found
return result; // Return the populated document property

Is it possible to write a generic function so that I don't have to rewrite the same exact code but with different models?
I tried with this code but it doesn't work:

export const findOrReturnInstance = async <T extends Document>(
  populatedDoc: PopulatedDoc<T>,
  model: Model<T>,
) => {
  if (populatedDoc instanceof model) {
    return populatedDoc;
  }
  const result = await model
    .findById(populatedDoc)
    .orFail();
  return result;
};

Is it the right approach or am I doing something wrong?
Attention, with this generic function I want to ensure that any virtual variables and instance methods, etc., are preserved.

Thank you for your help. 🙏🏻

@nikzanda nikzanda added help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted labels Dec 19, 2024
@vkarpov15 vkarpov15 added this to the 8.9.4 milestone Dec 27, 2024
@vkarpov15
Copy link
Collaborator

The approach is reasonable, you just need to type model correctly. I would also recommend against using PopulatedDoc<> type for reasons explained here.

Take a look at the following script, would this approach work for you?

import mongoose, { Schema } from 'mongoose';

const ParentModel = mongoose.model('Parent', new Schema({
  child: { type: Schema.Types.ObjectId, ref: 'Child' },
  name: String
}));

const childSchema: Schema = new Schema({ name: String });
const ChildModel = mongoose.model('Child', childSchema);
type ChildHydratedDocType = ReturnType<(typeof ChildModel)['hydrate']>;

(async function() {
  const doc = await ParentModel.findOne({}).populate<{ child: ChildHydratedDocType }>('child').orFail();
  const res1: ChildHydratedDocType = await findOrReturnInstance(doc.child, ChildModel);

  const doc2 = await ParentModel.findOne().orFail();
  const res2: ChildHydratedDocType = await findOrReturnInstance(doc.child, ChildModel);
})();

async function findOrReturnInstance<HydratedDocType extends mongoose.Document>(
  docOrId: HydratedDocType | mongoose.Types.ObjectId,
  Model: mongoose.Model<any, any, any, any, HydratedDocType>
) {
  if (docOrId instanceof mongoose.Document) {
    return docOrId;
  }
  
  return Model.findById(docOrId).orFail();
}  

@vkarpov15 vkarpov15 removed this from the 8.9.4 milestone Jan 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary
Projects
None yet
Development

No branches or pull requests

2 participants