I am having issues with mongoose queries when I query by an ObjectId field. Passing mongoose.Types.ObjectId
or string
both throws errors. TypeScript only stops complaining when I cast the ObjectId to mongoose.Schema.Types.ObjectId
, which then crashes at runtime since it's not a valid ObjectId.
interface DocumentWithTimestamp extends Document<ObjectId> {
createdAt: Date;
updatedAt: Date;
}
First of all, I noticed that Document defines _id
as ObjectId | undefined
, which already complicates everything.
I define my Schemas as follows:
export interface LanguageSchema extends DocumentWithTimestamp {
langCode: string;
locale: string;
}
interface LanguageModel extends Model<LanguageSchema> {
mapToLanguage(language: LeanDocument<LanguageSchema> | LanguageSchema): Language;
}
const languageSchema = new Schema<LanguageSchema, LanguageModel>(
{
langCode: { type: String, required: true, trim: true },
locale: { type: String, unique: true, required: true, trim: true },
},
{ collection: 'language', timestamps: true, versionKey: false },
);
languageSchema.statics.mapToLanguage = function (language: LeanDocument<LanguageSchema> | LanguageSchema): Language {
return {
id: language._id?.toString() || '', // why is _id conditional...?
langCode: language.langCode,
locale: language.locale,
};
};
export const LanguageModel = model<LanguageSchema, LanguageModel>('Language', languageSchema);
Now querying my LanguageModel by _id or any other ObjectId field throws type errors:
export async function findLanguagesByIds(languageIds: string[]) {
return LanguageModel.find({ _id: { $in: languageIds } }).lean();
}
Error:
Argument of type '{ _id: { $in: string[]; }; }' is not assignable to parameter of type 'Callback<LanguageSchema[]>'.
Object literal may only specify known properties, and '_id' does not exist in type 'Callback<LanguageSchema[]>'.
When I try to cast string[] to Schema.Types.ObjectId[] array, the error goes away but this cannot be the solution, since it crashes at runtime (Schema.Types.ObjectId is not a valid ObjectId).
const languages = await LanguageModel.find({
_id: { $in: languageIds.map((id) => new mongoose.Schema.Types.ObjectId(id)) },
}).lean();
If I cast to mongoose.Types.ObjectId
(to a real ObjectId) it throws errors again...
Any help is appreciated!
CodePudding user response:
I found the solution to my problem.
mongoose Document is defined as follows:
class Document<T = any, TQueryHelpers = any, DocType = any> {
constructor(doc?: any);
/** This documents _id. */
_id?: T;
// ...
}
I extended it using Document<ObjectId>
. The ObjectId type however was the Schema type, e.g.
import { ObjectId } from "mongoose";
But it actually should have been the actual ObjectId Type of mongoose and not the Schema type!
FIX:
import { Types } from "mongoose"
interface DocumentWithTimestamp extends Document<Types.ObjectId> {
createdAt: Date;
updatedAt: Date;
}