From 8825b76be5e901e57676b415cfdee2e7df123164 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 16 Oct 2024 14:12:57 -0400 Subject: [PATCH 1/2] types: make Buffers into mongodb.Binary in lean result type to match runtime behavior Fix #14902 --- test/types/schema.test.ts | 17 +++++++++++++++-- types/index.d.ts | 10 +++++++++- types/query.d.ts | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index ec20ce689e..f957232934 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -20,8 +20,10 @@ import { Types, Query, model, - ValidateOpts + ValidateOpts, + BufferToBinary } from 'mongoose'; +import { Binary } from 'mongodb'; import { IsPathRequired } from '../../types/inferschematype'; import { expectType, expectError, expectAssignable } from 'tsd'; import { ObtainDocumentPathType, ResolvePathType } from '../../types/inferschematype'; @@ -917,7 +919,7 @@ async function gh12593() { expectType(doc2.x); const doc3 = await Test.findOne({}).orFail().lean(); - expectType(doc3.x); + expectType(doc3.x); const arrSchema = new Schema({ arr: [{ type: Schema.Types.UUID }] }); @@ -1663,3 +1665,14 @@ async function gh14950() { expectType(doc.location!.type); expectType(doc.location!.coordinates); } + +async function gh14902() { + const def = { + image: { type: Buffer } + } as const; + const exampleSchema = new Schema(def); + const Test = model('Test', exampleSchema); + + const doc = await Test.findOne().lean().orFail(); + expectType(doc.image); +} diff --git a/types/index.d.ts b/types/index.d.ts index 3ec72ac281..521fe17f81 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -706,6 +706,14 @@ declare module 'mongoose' { [K in keyof T]: FlattenProperty; }; + export type BufferToBinary = T extends object ? { + [K in keyof T]: T[K] extends Buffer + ? mongodb.Binary + : T[K] extends (Buffer | null | undefined) + ? mongodb.Binary | null | undefined + : T[K]; + } : T; + /** * Separate type is needed for properties of union type (for example, Types.DocumentArray | undefined) to apply conditional check to each member of it * https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types @@ -716,7 +724,7 @@ declare module 'mongoose' { ? Types.DocumentArray> : FlattenMaps; export type actualPrimitives = string | boolean | number | bigint | symbol | null | undefined; - export type TreatAsPrimitives = actualPrimitives | NativeDate | RegExp | symbol | Error | BigInt | Types.ObjectId | Buffer | Function; + export type TreatAsPrimitives = actualPrimitives | NativeDate | RegExp | symbol | Error | BigInt | Types.ObjectId | Buffer | Function | mongodb.Binary; export type SchemaDefinitionType = T extends Document ? Omit> : T; diff --git a/types/query.d.ts b/types/query.d.ts index cb447f23c7..95b29aba4e 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -211,7 +211,7 @@ declare module 'mongoose' { type QueryOpThatReturnsDocument = 'find' | 'findOne' | 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; type GetLeanResultType = QueryOp extends QueryOpThatReturnsDocument - ? (ResultType extends any[] ? Require_id>[] : Require_id>) + ? (ResultType extends any[] ? Require_id>>[] : Require_id>>) : ResultType; type MergePopulatePaths> = QueryOp extends QueryOpThatReturnsDocument From 0b152ee9bbc98e73ed4ec1a7e0631ef03acd3b62 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 17 Oct 2024 10:53:34 -0400 Subject: [PATCH 2/2] recursively convert buffer to binary --- test/types/schema.test.ts | 13 +++++++++---- types/index.d.ts | 8 ++++++-- types/query.d.ts | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index f957232934..467e619236 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -1667,12 +1667,17 @@ async function gh14950() { } async function gh14902() { - const def = { - image: { type: Buffer } - } as const; - const exampleSchema = new Schema(def); + const exampleSchema = new Schema({ + image: { type: Buffer }, + subdoc: { + type: new Schema({ + testBuf: Buffer + }) + } + }); const Test = model('Test', exampleSchema); const doc = await Test.findOne().lean().orFail(); expectType(doc.image); + expectType(doc.subdoc!.testBuf); } diff --git a/types/index.d.ts b/types/index.d.ts index 521fe17f81..c6655b802f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -706,12 +706,16 @@ declare module 'mongoose' { [K in keyof T]: FlattenProperty; }; - export type BufferToBinary = T extends object ? { + export type BufferToBinary = T extends TreatAsPrimitives ? T : T extends Record ? { [K in keyof T]: T[K] extends Buffer ? mongodb.Binary : T[K] extends (Buffer | null | undefined) ? mongodb.Binary | null | undefined - : T[K]; + : T[K] extends Types.DocumentArray + ? Types.DocumentArray> + : T[K] extends Types.Subdocument + ? HydratedSingleSubdocument + : BufferToBinary; } : T; /** diff --git a/types/query.d.ts b/types/query.d.ts index 95b29aba4e..9d2114edff 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -211,7 +211,7 @@ declare module 'mongoose' { type QueryOpThatReturnsDocument = 'find' | 'findOne' | 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; type GetLeanResultType = QueryOp extends QueryOpThatReturnsDocument - ? (ResultType extends any[] ? Require_id>>[] : Require_id>>) + ? (ResultType extends any[] ? Require_id>>[] : Require_id>>) : ResultType; type MergePopulatePaths> = QueryOp extends QueryOpThatReturnsDocument