Description
Prerequisites
- I have written a descriptive issue title
- I have searched existing issues to ensure the bug has not already been reported
Mongoose version
6.9.0
Node.js version
16.18.1
MongoDB server version
4.4.18
Typescript version (if applicable)
4.9.4
Description
I'm encountering a few unexpected behaviors with the types being inferred from both schema helpers (InferSchemaType
and HydratedDocumentFromSchema
), the resulting types when executing a model method, and the resulting types when converting that result to a POJO., particularly as it pertains to subdocument arrays. The included sample code hopefully tells the story, but this is a summary of what I have found that feels incorrect.
Subdocument Array inference is returning types for _id
and __v
when using InferSchemaType
This is less of an error than a surprise based on what I read in #11967. As per that issue, InferSchemaType
does not return an _id
type. Although it's not indicated in that issue, it also doesn't have a type for __v
but that seems to be consistent with not having an _id
type. However, I was very surprised to see that subdocument arrays DO have a type for the _id
and the __v
value. It seems like neither the parent document nor the subdocuments should have these types.
Hydrated documents have wide types for _id
and __v
for subdocument arrays
I ran my schema through HydratedDocumentFromSchema
. This correctly provided a type of mongoose.types.ObjectId
for the parent document's _id
property. However, the subdocument arrays have a type of mongoose.Types.ObjectId | undefined
. I would have not expected this to be an optional property.
Additionally, the types for __v
on both the parent document as well as the subdocuments is any
with the optional flag. I traced through some history and it seems like any
was put there instead of number to try to avoid some other issues. I was surprised to see the optional flag on this type, although with any
it just widens it to any
, so maybe it doesn't matter. If this type ever was able to be more accurate and be number
then I wouldn't think this should be number | undefined
.
Document instance exhibits the same types as the HydratedDocumentFromSchema
This seems to track as I would expect them to be consistent with each other. However, all of what I described that feels off in that section also applies to the document instances.
Document instance converted to POJO additionally lacks a type for __v
After running a document instance through toObject()
, the resulting object does not have a type for the __v
property on the parent document, although it is a valid property on the subdocuments. This surprised me, because a type of LeanDocument<HydratedDocumentFromSchema<typeof schema>>
does have an __v
property, so there is a behavioral difference between those two things.
The POJO also continues to have the same types as HydratedDocumentFromSchema
otherwise the things I feel like are off with that are also off here.
Steps to Reproduce
import type { HydratedDocumentFromSchema, InferSchemaType, LeanDocument } from 'mongoose';
import mongoose, { Schema } from 'mongoose';
const subdocSchema = new Schema({ foo: { type: 'string', required: true } });
const schema = new Schema({
subdocArray: { type: [subdocSchema], required: true },
});
type Inferred = InferSchemaType<typeof schema>;
type InferrredId = Inferred['_id']; // ERROR (expected per #11967)
type InferredSubdocArrayId = Inferred['subdocArray'][number]['_id']; // Surprised -- mongoose.Types.ObjectId | undefined
type InferredVersion = Inferred['__v']; // ERROR (seems to be the same as #11967)
type InferredSubdocArrayVersion = Inferred['subdocArray'][number]['__v']; // Surprised -- any | undefined (widens to any)
type Hydrated = HydratedDocumentFromSchema<typeof schema>;
type HydratedId = Hydrated['_id']; // mongoose.Types.ObjectId (expected per #11967)
type HydratedSubdocArrayId = Hydrated['subdocArray'][number]['_id']; // mongoose.Types.ObjectId | undefined
type HydratedVersion = Hydrated['__v']; // any | undefined
type HydratedSubdocArrayVersion = Hydrated['subdocArray'][number]['__v']; // any | undefined (widens to any)
type LeanHydrated = LeanDocument<Hydrated>;
type LeanHydratedId = LeanHydrated['_id']; // mongoose.Types.ObjectId
type LeanHydratedSubdocArrayId = LeanHydrated['subdocArray'][number]['_id']; // mongoose.Types.ObjectId | undefined
type LeanHydratedVersion = LeanHydrated['__v']; // any | undefined
type LeanHydratedSubdocArrayVersion = LeanHydrated['subdocArray'][number]['__v']; // any | undefined (widens to any)
const Model = mongoose.model('test', schema);
const document = Model.findById('id')
.then((result) => {
if (result == null) {
return Promise.reject();
}
console.log(result._id); // mongoose.Types.ObjectId
console.log(result.__v); // any | undefined (widens to any);
console.log(result.subdocArray[0]._id); // mongoose.Types.ObjectId | undefined
console.log(result.subdocArray[0].__v); // any | undefined (widens to any);
return result.toObject();
})
.then((resultPOJO) => {
console.log(resultPOJO._id); // mongoose.Types.ObjectId
console.log(resultPOJO.__v); // ERROR (seems like this should be there?)
console.log(resultPOJO.subdocArray[0]._id); // mongoose.Types.ObjectID | undefined
console.log(resultPOJO.subdocArray[0].__v); // any | undefined (widens to any)
});
Expected Behavior
- Neither parent documents not subdocuments should have types for
_id
and__v
properties for consistency when usingInferSchemaType
. - The type of
_id
in a subdocument should be based on the schema options value for_id
. If the option is set tofalse
then there should be no_id
property on a subdocument. If the value is set totrue
or left as default then_id
should not be optional and should be an ObjectId. This applies to the inferred Hydrated types as well as the document instance types. - The type of
__v
in both parent and subdocuments should be based on the schema options value forskipVersioning
. IfskipVersioning
istrue
then there should be no__v
property. IfskipVersioning
isfalse
or left as default then__v
should be anumber
. This applies to the inferred Hydrated types as well as the document instance types. __v
should be a valid property for documents converted to POJO format.