mongoose 8.5.1 → 8.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/document.js CHANGED
@@ -1393,8 +1393,8 @@ Document.prototype.$set = function $set(path, val, type, options) {
1393
1393
  })();
1394
1394
 
1395
1395
  let didPopulate = false;
1396
- if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._id))) {
1397
- const unpopulatedValue = (schema && schema.$isSingleNested) ? schema.cast(val, this) : val._id;
1396
+ if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) {
1397
+ const unpopulatedValue = (schema && schema.$isSingleNested) ? schema.cast(val, this) : val._doc._id;
1398
1398
  this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor });
1399
1399
  val.$__.wasPopulated = { value: unpopulatedValue };
1400
1400
  didPopulate = true;
@@ -1409,10 +1409,10 @@ Document.prototype.$set = function $set(path, val, type, options) {
1409
1409
  schema.options[typeKey][0].ref &&
1410
1410
  _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) {
1411
1411
  popOpts = { [populateModelSymbol]: val[0].constructor };
1412
- this.$populated(path, val.map(function(v) { return v._id; }), popOpts);
1412
+ this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts);
1413
1413
 
1414
1414
  for (const doc of val) {
1415
- doc.$__.wasPopulated = { value: doc._id };
1415
+ doc.$__.wasPopulated = { value: doc._doc._id };
1416
1416
  }
1417
1417
  didPopulate = true;
1418
1418
  }
@@ -1455,7 +1455,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1455
1455
  if (Array.isArray(val) && this.$__.populated[path]) {
1456
1456
  for (let i = 0; i < val.length; ++i) {
1457
1457
  if (val[i] instanceof Document) {
1458
- val.set(i, val[i]._id, true);
1458
+ val.set(i, val[i]._doc._id, true);
1459
1459
  }
1460
1460
  }
1461
1461
  }
@@ -1628,7 +1628,7 @@ Document.prototype.$__shouldModify = function(pathToMark, path, options, constru
1628
1628
  // if they have the same _id
1629
1629
  if (this.$populated(path) &&
1630
1630
  val instanceof Document &&
1631
- deepEqual(val._id, priorVal)) {
1631
+ deepEqual(val._doc._id, priorVal)) {
1632
1632
  return false;
1633
1633
  }
1634
1634
 
@@ -2770,15 +2770,6 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2770
2770
  }
2771
2771
  }
2772
2772
 
2773
- for (const path of paths) {
2774
- // Single nested paths (paths embedded under single nested subdocs) will
2775
- // be validated on their own when we call `validate()` on the subdoc itself.
2776
- // Re: gh-8468
2777
- if (doc.$__schema.singleNestedPaths.hasOwnProperty(path)) {
2778
- paths.delete(path);
2779
- continue;
2780
- }
2781
- }
2782
2773
 
2783
2774
  if (Array.isArray(pathsToValidate)) {
2784
2775
  paths = _handlePathsToValidate(paths, pathsToValidate);
@@ -2800,7 +2791,10 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2800
2791
  _v = _v.toObject({ transform: false });
2801
2792
  }
2802
2793
  const flat = flatten(_v, pathToCheck, flattenOptions, doc.$__schema);
2803
- Object.keys(flat).forEach(addToPaths);
2794
+ // Single nested paths (paths embedded under single nested subdocs) will
2795
+ // be validated on their own when we call `validate()` on the subdoc itself.
2796
+ // Re: gh-8468
2797
+ Object.keys(flat).filter(path => !doc.$__schema.singleNestedPaths.hasOwnProperty(path)).forEach(addToPaths);
2804
2798
  }
2805
2799
  }
2806
2800
 
@@ -3801,7 +3795,7 @@ Document.prototype.$__handleReject = function handleReject(err) {
3801
3795
  };
3802
3796
 
3803
3797
  /**
3804
- * Internal helper for toObject() and toJSON() that doesn't manipulate options
3798
+ * Internal common logic for toObject() and toJSON()
3805
3799
  *
3806
3800
  * @return {Object}
3807
3801
  * @api private
@@ -3834,13 +3828,16 @@ Document.prototype.$toObject = function(options, json) {
3834
3828
  }
3835
3829
 
3836
3830
  const depopulate = options._calledWithOptions.depopulate
3837
- ?? options._parentOptions?.depopulate
3838
3831
  ?? defaultOptions?.depopulate
3832
+ ?? options.depopulate
3839
3833
  ?? false;
3840
3834
  // _isNested will only be true if this is not the top level document, we
3841
3835
  // should never depopulate the top-level document
3842
3836
  if (depopulate && options._isNested && this.$__.wasPopulated) {
3843
- return clone(this.$__.wasPopulated.value || this._id, options);
3837
+ return clone(this.$__.wasPopulated.value || this._doc._id, options);
3838
+ }
3839
+ if (depopulate) {
3840
+ options.depopulate = true;
3844
3841
  }
3845
3842
 
3846
3843
  // merge default options with input options.
@@ -3855,7 +3852,9 @@ Document.prototype.$toObject = function(options, json) {
3855
3852
  options.json = json;
3856
3853
  options.minimize = _minimize;
3857
3854
 
3858
- options._parentOptions = options;
3855
+ const parentOptions = options._parentOptions;
3856
+ // Parent options should only bubble down for subdocuments, not populated docs
3857
+ options._parentOptions = this.$isSubdocument ? options : null;
3859
3858
 
3860
3859
  options._skipSingleNestedGetters = false;
3861
3860
  // remember the root transform function
@@ -3886,6 +3885,7 @@ Document.prototype.$toObject = function(options, json) {
3886
3885
 
3887
3886
  const virtuals = options._calledWithOptions.virtuals
3888
3887
  ?? defaultOptions.virtuals
3888
+ ?? parentOptions?.virtuals
3889
3889
  ?? undefined;
3890
3890
 
3891
3891
  if (virtuals || (getters && virtuals !== false)) {
@@ -4768,7 +4768,23 @@ Document.prototype.depopulate = function(path) {
4768
4768
  continue;
4769
4769
  }
4770
4770
  delete populated[key];
4771
- utils.setValue(key, populatedIds, this._doc);
4771
+ if (Array.isArray(populatedIds)) {
4772
+ const arr = utils.getValue(key, this._doc);
4773
+ if (arr.isMongooseArray) {
4774
+ const rawArray = arr.__array;
4775
+ for (let i = 0; i < rawArray.length; ++i) {
4776
+ const subdoc = rawArray[i];
4777
+ if (subdoc == null) {
4778
+ continue;
4779
+ }
4780
+ rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id;
4781
+ }
4782
+ } else {
4783
+ utils.setValue(key, populatedIds, this._doc);
4784
+ }
4785
+ } else {
4786
+ utils.setValue(key, populatedIds, this._doc);
4787
+ }
4772
4788
  }
4773
4789
  return this;
4774
4790
  }
@@ -4781,7 +4797,23 @@ Document.prototype.depopulate = function(path) {
4781
4797
  delete this.$$populatedVirtuals[singlePath];
4782
4798
  delete this._doc[singlePath];
4783
4799
  } else if (populatedIds) {
4784
- utils.setValue(singlePath, populatedIds, this._doc);
4800
+ if (Array.isArray(populatedIds)) {
4801
+ const arr = utils.getValue(singlePath, this._doc);
4802
+ if (arr.isMongooseArray) {
4803
+ const rawArray = arr.__array;
4804
+ for (let i = 0; i < rawArray.length; ++i) {
4805
+ const subdoc = rawArray[i];
4806
+ if (subdoc == null) {
4807
+ continue;
4808
+ }
4809
+ rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id;
4810
+ }
4811
+ } else {
4812
+ utils.setValue(singlePath, populatedIds, this._doc);
4813
+ }
4814
+ } else {
4815
+ utils.setValue(singlePath, populatedIds, this._doc);
4816
+ }
4785
4817
  }
4786
4818
  }
4787
4819
  return this;
@@ -17,7 +17,7 @@ const trustedSymbol = require('./query/trusted').trustedSymbol;
17
17
  *
18
18
  * If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible.
19
19
  *
20
- * Functions are never cloned.
20
+ * Functions and primitives are never cloned.
21
21
  *
22
22
  * @param {Object} obj the object to clone
23
23
  * @param {Object} options
@@ -30,6 +30,9 @@ function clone(obj, options, isArrayChild) {
30
30
  if (obj == null) {
31
31
  return obj;
32
32
  }
33
+ if (typeof obj === 'number' || typeof obj === 'string' || typeof obj === 'boolean' || typeof obj === 'bigint') {
34
+ return obj;
35
+ }
33
36
 
34
37
  if (Array.isArray(obj)) {
35
38
  return cloneArray(isMongooseArray(obj) ? obj.__array : obj, options);
@@ -148,13 +151,12 @@ function cloneObject(obj, options, isArrayChild) {
148
151
  ret[trustedSymbol] = obj[trustedSymbol];
149
152
  }
150
153
 
151
- let i = 0;
152
- let key = '';
153
154
  const keys = Object.keys(obj);
154
155
  const len = keys.length;
155
156
 
156
- for (i = 0; i < len; ++i) {
157
- if (specialProperties.has(key = keys[i])) {
157
+ for (let i = 0; i < len; ++i) {
158
+ const key = keys[i];
159
+ if (specialProperties.has(key)) {
158
160
  continue;
159
161
  }
160
162
 
@@ -93,8 +93,12 @@ function _castExpression(val, schema, strictQuery) {
93
93
  } else if (val.$ifNull != null) {
94
94
  val.$ifNull.map(v => _castExpression(v, schema, strictQuery));
95
95
  } else if (val.$switch != null) {
96
- val.branches.map(v => _castExpression(v, schema, strictQuery));
97
- val.default = _castExpression(val.default, schema, strictQuery);
96
+ if (Array.isArray(val.$switch.branches)) {
97
+ val.$switch.branches = val.$switch.branches.map(v => _castExpression(v, schema, strictQuery));
98
+ }
99
+ if ('default' in val.$switch) {
100
+ val.$switch.default = _castExpression(val.$switch.default, schema, strictQuery);
101
+ }
98
102
  }
99
103
 
100
104
  const keys = Object.keys(val);
package/lib/model.js CHANGED
@@ -290,9 +290,10 @@ Model.prototype.$__handleSave = function(options, callback) {
290
290
 
291
291
  const session = this.$session();
292
292
  const asyncLocalStorage = this[modelDbSymbol].base.transactionAsyncLocalStorage?.getStore();
293
- if (!saveOptions.hasOwnProperty('session') && session != null) {
293
+ if (session != null) {
294
294
  saveOptions.session = session;
295
- } else if (asyncLocalStorage?.session != null) {
295
+ } else if (!options.hasOwnProperty('session') && asyncLocalStorage?.session != null) {
296
+ // Only set session from asyncLocalStorage if `session` option wasn't originally passed in options
296
297
  saveOptions.session = asyncLocalStorage.session;
297
298
  }
298
299
  if (this.$isNew) {
@@ -3630,7 +3631,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
3630
3631
  const len = paths.length;
3631
3632
 
3632
3633
  for (let i = 0; i < len; ++i) {
3633
- where[paths[i]] = shardKey[paths[i]];
3634
+ where[paths[i]] = document[paths[i]];
3634
3635
  }
3635
3636
  }
3636
3637
 
@@ -4458,7 +4459,7 @@ function _assign(model, vals, mod, assignmentOpts) {
4458
4459
  }
4459
4460
  } else {
4460
4461
  if (_val instanceof Document) {
4461
- _val = _val._id;
4462
+ _val = _val._doc._id;
4462
4463
  }
4463
4464
  key = String(_val);
4464
4465
  if (rawDocs[key]) {
@@ -4467,7 +4468,7 @@ function _assign(model, vals, mod, assignmentOpts) {
4467
4468
  rawOrder[key].push(i);
4468
4469
  } else if (isVirtual ||
4469
4470
  rawDocs[key].constructor !== val.constructor ||
4470
- String(rawDocs[key]._id) !== String(val._id)) {
4471
+ (rawDocs[key] instanceof Document ? String(rawDocs[key]._doc._id) : String(rawDocs[key]._id)) !== (val instanceof Document ? String(val._doc._id) : String(val._id))) {
4471
4472
  // May need to store multiple docs with the same id if there's multiple models
4472
4473
  // if we have discriminators or a ref function. But avoid converting to an array
4473
4474
  // if we have multiple queries on the same model because of `perDocumentLimit` re: gh-9906
@@ -69,7 +69,7 @@ function SchemaDocumentArray(key, schema, options, schemaOptions) {
69
69
 
70
70
  const fn = this.defaultValue;
71
71
 
72
- if (!('defaultValue' in this) || fn !== void 0) {
72
+ if (!('defaultValue' in this) || fn != null) {
73
73
  this.default(function() {
74
74
  let arr = fn.call(this);
75
75
  if (arr != null && !Array.isArray(arr)) {
package/lib/schemaType.js CHANGED
@@ -1542,7 +1542,7 @@ SchemaType.prototype._castRef = function _castRef(value, doc, init) {
1542
1542
  }
1543
1543
 
1544
1544
  if (value.$__ != null) {
1545
- value.$__.wasPopulated = value.$__.wasPopulated || { value: value._id };
1545
+ value.$__.wasPopulated = value.$__.wasPopulated || { value: value._doc._id };
1546
1546
  return value;
1547
1547
  }
1548
1548
 
@@ -1568,7 +1568,7 @@ SchemaType.prototype._castRef = function _castRef(value, doc, init) {
1568
1568
  !doc.$__.populated[path].options.options ||
1569
1569
  !doc.$__.populated[path].options.options.lean) {
1570
1570
  ret = new pop.options[populateModelSymbol](value);
1571
- ret.$__.wasPopulated = { value: ret._id };
1571
+ ret.$__.wasPopulated = { value: ret._doc._id };
1572
1572
  }
1573
1573
 
1574
1574
  return ret;
@@ -721,6 +721,7 @@ const methods = {
721
721
  }
722
722
 
723
723
  this._registerAtomic('$push', atomic);
724
+
724
725
  return ret;
725
726
  },
726
727
 
package/lib/types/map.js CHANGED
@@ -68,10 +68,17 @@ class MongooseMap extends Map {
68
68
  * and change tracking. Note that Mongoose maps _only_ support strings and
69
69
  * ObjectIds as keys.
70
70
  *
71
+ * Keys also cannot:
72
+ * - be named after special properties `prototype`, `constructor`, and `__proto__`
73
+ * - start with a dollar sign (`$`)
74
+ * - contain any dots (`.`)
75
+ *
71
76
  * #### Example:
72
77
  *
73
78
  * doc.myMap.set('test', 42); // works
74
79
  * doc.myMap.set({ obj: 42 }, 42); // Throws "Mongoose maps only support string keys"
80
+ * doc.myMap.set(10, 42); // Throws "Mongoose maps only support string keys"
81
+ * doc.myMap.set("$test", 42); // Throws "Mongoose maps do not support keys that start with "$", got "$test""
75
82
  *
76
83
  * @api public
77
84
  * @method set
@@ -116,7 +123,7 @@ class MongooseMap extends Map {
116
123
  v = new populated.options[populateModelSymbol](v);
117
124
  }
118
125
  // Doesn't support single nested "in-place" populate
119
- v.$__.wasPopulated = { value: v._id };
126
+ v.$__.wasPopulated = { value: v._doc._id };
120
127
  return v;
121
128
  });
122
129
  } else if (value != null) {
@@ -124,7 +131,7 @@ class MongooseMap extends Map {
124
131
  value = new populated.options[populateModelSymbol](value);
125
132
  }
126
133
  // Doesn't support single nested "in-place" populate
127
- value.$__.wasPopulated = { value: value._id };
134
+ value.$__.wasPopulated = { value: value._doc._id };
128
135
  }
129
136
  } else {
130
137
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.5.1",
4
+ "version": "8.5.3",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "@babel/core": "7.24.7",
32
- "@babel/preset-env": "7.24.7",
32
+ "@babel/preset-env": "7.25.3",
33
33
  "@typescript-eslint/eslint-plugin": "^6.21.0",
34
34
  "@typescript-eslint/parser": "^6.21.0",
35
35
  "acquit": "1.3.0",
@@ -49,15 +49,15 @@
49
49
  "eslint-plugin-mocha-no-only": "1.2.0",
50
50
  "express": "^4.19.2",
51
51
  "fs-extra": "~11.2.0",
52
- "highlight.js": "11.9.0",
52
+ "highlight.js": "11.10.0",
53
53
  "lodash.isequal": "4.5.0",
54
54
  "lodash.isequalwith": "4.4.0",
55
55
  "markdownlint-cli2": "^0.13.0",
56
56
  "marked": "4.3.0",
57
57
  "mkdirp": "^3.0.1",
58
- "mocha": "10.6.0",
58
+ "mocha": "10.7.0",
59
59
  "moment": "2.30.1",
60
- "mongodb-memory-server": "9.4.0",
60
+ "mongodb-memory-server": "10.0.0",
61
61
  "ncp": "^2.0.0",
62
62
  "nyc": "15.1.0",
63
63
  "pug": "3.0.3",
@@ -65,9 +65,9 @@
65
65
  "sinon": "18.0.0",
66
66
  "stream-browserify": "3.0.0",
67
67
  "tsd": "0.31.1",
68
- "typescript": "5.5.3",
68
+ "typescript": "5.5.4",
69
69
  "uuid": "10.0.0",
70
- "webpack": "5.92.1"
70
+ "webpack": "5.93.0"
71
71
  },
72
72
  "directories": {
73
73
  "lib": "./lib/mongoose"
@@ -72,7 +72,7 @@ declare module 'mongoose' {
72
72
  readonly config: any;
73
73
 
74
74
  /** The mongodb.Db instance, set when the connection is opened */
75
- readonly db: mongodb.Db;
75
+ readonly db: mongodb.Db | undefined;
76
76
 
77
77
  /**
78
78
  * Helper for `createCollection()`. Will explicitly create the given collection
package/types/cursor.d.ts CHANGED
@@ -51,7 +51,7 @@ declare module 'mongoose' {
51
51
  * Get the next document from this cursor. Will return `null` when there are
52
52
  * no documents left.
53
53
  */
54
- next(): Promise<DocType>;
54
+ next(): Promise<DocType | null>;
55
55
 
56
56
  options: Options;
57
57
  }
package/types/index.d.ts CHANGED
@@ -157,8 +157,37 @@ declare module 'mongoose' {
157
157
  >
158
158
  >
159
159
  >;
160
- export type HydratedSingleSubdocument<DocType, TOverrides = {}> = Types.Subdocument<unknown, Record<string, never>, DocType> & Require_id<DocType> & TOverrides;
161
- export type HydratedArraySubdocument<DocType, TOverrides = {}> = Types.ArraySubdocument<unknown, Record<string, never>, DocType> & Require_id<DocType> & TOverrides;
160
+ export type HydratedSingleSubdocument<
161
+ DocType,
162
+ TOverrides = {}
163
+ > = IfAny<
164
+ DocType,
165
+ any,
166
+ TOverrides extends Record<string, never> ?
167
+ Types.Subdocument<unknown, Record<string, never>, DocType> & Require_id<DocType> :
168
+ IfAny<
169
+ TOverrides,
170
+ Types.Subdocument<unknown, Record<string, never>, DocType> & Require_id<DocType>,
171
+ Types.Subdocument<unknown, Record<string, never>, DocType> & MergeType<
172
+ Require_id<DocType>,
173
+ TOverrides
174
+ >
175
+ >
176
+ >;
177
+ export type HydratedArraySubdocument<DocType, TOverrides = {}> = IfAny<
178
+ DocType,
179
+ any,
180
+ TOverrides extends Record<string, never> ?
181
+ Types.ArraySubdocument<unknown, Record<string, never>, DocType> & Require_id<DocType> :
182
+ IfAny<
183
+ TOverrides,
184
+ Types.ArraySubdocument<unknown, Record<string, never>, DocType> & Require_id<DocType>,
185
+ Types.ArraySubdocument<unknown, Record<string, never>, DocType> & MergeType<
186
+ Require_id<DocType>,
187
+ TOverrides
188
+ >
189
+ >
190
+ >;
162
191
 
163
192
  export type HydratedDocumentFromSchema<TSchema extends Schema> = HydratedDocument<
164
193
  InferSchemaType<TSchema>,
@@ -10,12 +10,12 @@ declare module 'mongoose' {
10
10
  export type InferRawDocType<
11
11
  DocDefinition,
12
12
  TSchemaOptions extends Record<any, any> = DefaultSchemaOptions
13
- > = {
13
+ > = ApplySchemaOptions<{
14
14
  [
15
15
  K in keyof (RequiredPaths<DocDefinition, TSchemaOptions['typeKey']> &
16
16
  OptionalPaths<DocDefinition, TSchemaOptions['typeKey']>)
17
17
  ]: ObtainRawDocumentPathType<DocDefinition[K], TSchemaOptions['typeKey']>;
18
- };
18
+ }, TSchemaOptions>;
19
19
 
20
20
  /**
21
21
  * @summary Obtains schema Path type.
@@ -74,14 +74,25 @@ declare module 'mongoose' {
74
74
 
75
75
  type ApplySchemaOptions<T, O = DefaultSchemaOptions> = ResolveTimestamps<T, O>;
76
76
 
77
- type ResolveTimestamps<T, O> = O extends { timestamps: true }
77
+ type ResolveTimestamps<T, O> = O extends { methods: any } | { statics: any } | { virtuals: any } | { timestamps?: false } ? T
78
78
  // For some reason, TypeScript sets all the document properties to unknown
79
79
  // if we use methods, statics, or virtuals. So avoid inferring timestamps
80
80
  // if any of these are set for now. See gh-12807
81
- ? O extends { methods: any } | { statics: any } | { virtuals: any }
82
- ? T
83
- : { createdAt: NativeDate; updatedAt: NativeDate; } & T
84
- : T;
81
+ : O extends { timestamps: infer TimestampOptions } ? TimestampOptions extends true
82
+ ? { createdAt: NativeDate; updatedAt: NativeDate; } & T
83
+ : TimestampOptions extends SchemaTimestampsConfig
84
+ ? {
85
+ -readonly [K in keyof Pick<
86
+ TimestampOptions,
87
+ 'createdAt' | 'updatedAt'
88
+ > as TimestampOptions[K] extends true
89
+ ? K
90
+ : TimestampOptions[K] extends string
91
+ ? TimestampOptions[K]
92
+ : never]: NativeDate;
93
+ } & T
94
+ : T
95
+ : T;
85
96
  }
86
97
 
87
98
  type IsPathDefaultUndefined<PathType> = PathType extends { default: undefined } ?
@@ -220,7 +220,8 @@ declare module 'mongoose' {
220
220
  OptionsConstructor: SchemaTypeOptions<T>;
221
221
 
222
222
  /** Cast `val` to this schema type. Each class that inherits from schema type should implement this function. */
223
- cast(val: any, doc: Document<any>, init: boolean, prev?: any, options?: any): any;
223
+ cast(val: any, doc?: Document<any>, init?: boolean, prev?: any, options?: any): any;
224
+ cast<ResultType>(val: any, doc?: Document<any>, init?: boolean, prev?: any, options?: any): ResultType;
224
225
 
225
226
  /** Sets a default value for this SchemaType. */
226
227
  default(val: any): any;
@@ -443,7 +444,7 @@ declare module 'mongoose' {
443
444
  defaultOptions: Record<string, any>;
444
445
  }
445
446
 
446
- class Subdocument extends SchemaType implements AcceptsDiscriminator {
447
+ class Subdocument<DocType = unknown> extends SchemaType implements AcceptsDiscriminator {
447
448
  /** This schema type's name, to defend against minifiers that mangle function names. */
448
449
  static schemaName: string;
449
450
 
@@ -455,6 +456,8 @@ declare module 'mongoose' {
455
456
 
456
457
  discriminator<T, U>(name: string | number, schema: Schema<T, U>, value?: string): U;
457
458
  discriminator<D>(name: string | number, schema: Schema, value?: string): Model<D>;
459
+
460
+ cast(val: any, doc?: Document<any>, init?: boolean, prev?: any, options?: any): HydratedSingleSubdocument<DocType>;
458
461
  }
459
462
 
460
463
  class String extends SchemaType {