From d348ce96480da6e1ceac52bb01e143450d781e84 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 11 Aug 2023 10:25:00 -0400 Subject: [PATCH] types(models+query): return `lean` type when passing QueryOptions with `lean: true` to relevant model functions like `find()` and `findOne()` Fix #13705 --- test/types/models.test.ts | 36 ++++++++++++++- types/models.d.ts | 96 +++++++++++++++++++++++++++++++++++++++ types/query.d.ts | 18 +++++++- 3 files changed, 147 insertions(+), 3 deletions(-) diff --git a/test/types/models.test.ts b/test/types/models.test.ts index 2bd33169ede..038c372656b 100644 --- a/test/types/models.test.ts +++ b/test/types/models.test.ts @@ -1,4 +1,4 @@ -import { +import mongoose, { Schema, Document, Model, @@ -639,3 +639,37 @@ function gh13529() { resourceDoc.foo = 'bar'; } } + +async function gh13705() { + const schema = new Schema({ name: String }); + const TestModel = model('Test', schema); + + type ExpectedLeanDoc = (mongoose.FlattenMaps<{ name?: string }> & { _id: mongoose.Types.ObjectId }); + + const findByIdRes = await TestModel.findById('0'.repeat(24), undefined, { lean: true }); + expectType(findByIdRes); + + const findOneRes = await TestModel.findOne({ _id: '0'.repeat(24) }, undefined, { lean: true }); + expectType(findOneRes); + + const findRes = await TestModel.find({ _id: '0'.repeat(24) }, undefined, { lean: true }); + expectType(findRes); + + const findByIdAndDeleteRes = await TestModel.findByIdAndDelete('0'.repeat(24), { lean: true }); + expectType(findByIdAndDeleteRes); + + const findByIdAndRemoveRes = await TestModel.findByIdAndRemove('0'.repeat(24), { lean: true }); + expectType(findByIdAndRemoveRes); + + const findByIdAndUpdateRes = await TestModel.findByIdAndUpdate('0'.repeat(24), {}, { lean: true }); + expectType(findByIdAndUpdateRes); + + const findOneAndDeleteRes = await TestModel.findOneAndDelete({ _id: '0'.repeat(24) }, { lean: true }); + expectType(findOneAndDeleteRes); + + const findOneAndReplaceRes = await TestModel.findOneAndReplace({ _id: '0'.repeat(24) }, {}, { lean: true }); + expectType(findOneAndReplaceRes); + + const findOneAndUpdateRes = await TestModel.findOneAndUpdate({}, {}, { lean: true }); + expectType(findOneAndUpdateRes); +} diff --git a/types/models.d.ts b/types/models.d.ts index 50d3cc220fa..6674db22fff 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -297,6 +297,17 @@ declare module 'mongoose' { * equivalent to `findOne({ _id: id })`. If you want to query by a document's * `_id`, use `findById()` instead of `findOne()`. */ + findById( + id: any, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOne' + >; findById( id: any, projection?: ProjectionType | null, @@ -308,6 +319,17 @@ declare module 'mongoose' { ): QueryWithHelpers; /** Finds one document. */ + findOne( + filter: FilterQuery, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOne' + >; findOne( filter?: FilterQuery, projection?: ProjectionType | null, @@ -460,6 +482,17 @@ declare module 'mongoose' { >; /** Creates a `find` query: gets a list of documents that match `filter`. */ + find( + filter: FilterQuery, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'find' + >; find( filter: FilterQuery, projection?: ProjectionType | null | undefined, @@ -476,18 +509,49 @@ declare module 'mongoose' { ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'find'>; /** Creates a `findByIdAndDelete` query, filtering by the given `_id`. */ + findByIdAndDelete( + id: mongodb.ObjectId | any, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndDelete' + >; findByIdAndDelete( id?: mongodb.ObjectId | any, options?: QueryOptions | null ): QueryWithHelpers; /** Creates a `findByIdAndRemove` query, filtering by the given `_id`. */ + findByIdAndRemove( + id: mongodb.ObjectId | any, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndDelete' + >; findByIdAndRemove( id?: mongodb.ObjectId | any, options?: QueryOptions | null ): QueryWithHelpers; /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ + findByIdAndUpdate( + id: mongodb.ObjectId | any, + update: UpdateQuery, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndUpdate' + >; findByIdAndUpdate( id: mongodb.ObjectId | any, update: UpdateQuery, @@ -509,6 +573,16 @@ declare module 'mongoose' { ): QueryWithHelpers; /** Creates a `findOneAndDelete` query: atomically finds the given document, deletes it, and returns the document as it was before deletion. */ + findOneAndDelete( + filter: FilterQuery, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndDelete' + >; findOneAndDelete( filter?: FilterQuery, options?: QueryOptions | null @@ -521,6 +595,17 @@ declare module 'mongoose' { ): QueryWithHelpers; /** Creates a `findOneAndReplace` query: atomically finds the given document and replaces it with `replacement`. */ + findOneAndReplace( + filter: FilterQuery, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndReplace' + >; findOneAndReplace( filter: FilterQuery, replacement: TRawDocType | AnyObject, @@ -538,6 +623,17 @@ declare module 'mongoose' { ): QueryWithHelpers; /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ + findOneAndUpdate( + filter: FilterQuery, + update: UpdateQuery, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndUpdate' + >; findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, diff --git a/types/query.d.ts b/types/query.d.ts index 0958e30dc9b..3963481a20b 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -179,6 +179,10 @@ declare module 'mongoose' { type QueryOpThatReturnsDocument = 'find' | 'findOne' | 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; + type GetLeanResultType = QueryOp extends QueryOpThatReturnsDocument + ? (ResultType extends any[] ? Require_id>[] : Require_id>) + : ResultType; + class Query implements SessionOperation { _mongooseOptions: MongooseQueryOptions; @@ -499,9 +503,19 @@ declare module 'mongoose' { j(val: boolean | null): this; /** Sets the lean option. */ - lean>[] : Require_id>) : ResultType>( + lean< + LeanResultType = GetLeanResultType + >( val?: boolean | any - ): QueryWithHelpers; + ): QueryWithHelpers< + ResultType extends null + ? LeanResultType | null + : LeanResultType, + DocType, + THelpers, + RawDocType, + QueryOp + >; /** Specifies the maximum number of documents the query will return. */ limit(val: number): this;