From 68716d6312e649c3c9d76f3344cd9af7e5366325 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 10 Aug 2025 21:01:57 -0400 Subject: [PATCH] types: remove logic that omits timestamps when virtuals, methods, etc. options set Fix #12807 --- docs/typescript/schemas.md | 2 +- test/types/schema.test.ts | 12 +++++++----- types/inferschematype.d.ts | 5 +---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/typescript/schemas.md b/docs/typescript/schemas.md index de53923ced6..e1b9361c95b 100644 --- a/docs/typescript/schemas.md +++ b/docs/typescript/schemas.md @@ -30,7 +30,7 @@ There are a few caveats for using automatic type inference: 1. You need to set `strictNullChecks: true` or `strict: true` in your `tsconfig.json`. Or, if you're setting flags at the command line, `--strictNullChecks` or `--strict`. There are [known issues](https://github.com/Automattic/mongoose/issues/12420) with automatic type inference with strict mode disabled. 2. You need to define your schema in the `new Schema()` call. Don't assign your schema definition to a temporary variable. Doing something like `const schemaDefinition = { name: String }; const schema = new Schema(schemaDefinition);` will not work. -3. Mongoose adds `createdAt` and `updatedAt` to your schema if you specify the `timestamps` option in your schema, *except* if you also specify `methods`, `virtuals`, or `statics`. There is a [known issue](https://github.com/Automattic/mongoose/issues/12807) with type inference with timestamps and methods/virtuals/statics options. If you use methods, virtuals, and statics, you're responsible for adding `createdAt` and `updatedAt` to your schema definition. +3. Mongoose adds `createdAt` and `updatedAt` to your schema if you specify the `timestamps` option in your schema. If you must define your schema separately, use [as const](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) (`const schemaDefinition = { ... } as const;`) to prevent *type widening*. TypeScript will automatically widen types like `required: false` to `required: boolean`, which will cause Mongoose to assume the field is required. Using `as const` forces TypeScript to retain these types. diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index e798d4bb50a..2d15c3cd24d 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -899,11 +899,13 @@ function testInferTimestamps() { }); type WithTimestamps2 = InferSchemaType; - // For some reason, expectType<{ createdAt: Date, updatedAt: Date, name?: string }> throws - // an error "Parameter type { createdAt: Date; updatedAt: Date; name?: string | undefined; } - // is not identical to argument type { createdAt: NativeDate; updatedAt: NativeDate; } & - // { name?: string | undefined; }" - expectType<{ name?: string | null }>({} as WithTimestamps2); + expectType<{ createdAt: Date; updatedAt: Date } & { name?: string | null }>({} as WithTimestamps2); + + const TestModel = model('Test', schema2); + const doc = new TestModel({ name: 'test' }); + expectType(doc.name); + + expectType(doc.myName()); } function gh12431() { diff --git a/types/inferschematype.d.ts b/types/inferschematype.d.ts index 3dbcb47d1b1..521bd5f3b9a 100644 --- a/types/inferschematype.d.ts +++ b/types/inferschematype.d.ts @@ -75,10 +75,7 @@ declare module 'mongoose' { type ApplySchemaOptions = ResolveTimestamps; - type ResolveTimestamps = O extends { methods: any } | { statics: any } | { virtuals: any } | { timestamps?: false } ? T - // For some reason, TypeScript sets all the document properties to unknown - // if we use methods, statics, or virtuals. So avoid inferring timestamps - // if any of these are set for now. See gh-12807 + type ResolveTimestamps = O extends { timestamps?: false } ? T : O extends { timestamps: infer TimestampOptions } ? TimestampOptions extends true ? { createdAt: NativeDate; updatedAt: NativeDate; } & T : TimestampOptions extends SchemaTimestampsConfig