I am wondering what is the best way to make Typescript notice the Mongoose populate method.
This is my document interface:
export interface TestDoc extends mongoose.Document {
name: string
createdBy: ID | UserDoc
allowedLanguages: TestLanguageName[]
problems: ID[] | ProblemDoc[]
candidates: string[]
invited: number
completed: number
incomplete: number
addCandidate: (candidateId: string) => void
}
and this is the problematic part:
const test = await Test.findOne({
_id,
createdBy: req.user._id
}).populate('createdBy').exec()
my main goal is that when we execute a populate method, test.createdBy should become a type of UserDoc, otherwise it should be an ID.
after some googling, I ran into this solution:
export type Populated<M, K extends keyof M> = Omit<M, K> &
{
[P in K]: Exclude<M[P], ID[] | ID>
}
that makes it really straightforward:
const test = await Test.findOne({
_id,
createdBy: req.user._id
}).populate('createdBy').exec() as Populated<TestDoc, 'createdBy' | 'problems'>
however there is one more problem - nested fields, we might want to populate a field inside of another populated document. For example:
const tests = await Test.find({
createdBy: req.user._id
})
.skip(offset ? parseInt(offset, 10) : 0)
.limit(limit ? parseInt(limit, 10) : 0)
.populate({
path: 'candidates',
populate: {
path: 'submittedProblems.problem'
}
})
.exec()
since I haven't managed to solve this using Populated interface, that makes me question the approach now :( maybe this is not the way.
How would you guys deal with this? what is the best practice? I checked out Typegoose, but it seems to have same issue.