Skip to content

Commit

Permalink
fix(index.d.ts): make query methods return QueryWithHelpers so quer…
Browse files Browse the repository at this point in the history
…y helpers pass through chaining

Fix #10040
  • Loading branch information
vkarpov15 committed Mar 22, 2021
1 parent 04f880f commit 96f7905
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 37 deletions.
66 changes: 34 additions & 32 deletions index.d.ts
Expand Up @@ -1064,6 +1064,8 @@ declare module 'mongoose' {
type SchemaPreOptions = { document?: boolean, query?: boolean };
type SchemaPostOptions = { document?: boolean, query?: boolean };

type ExtractQueryHelpers<M> = M extends Model<any, infer TQueryHelpers> ? TQueryHelpers : {};

class Schema<DocType extends Document = Document, M extends Model<DocType, any> = Model<any, any>, SchemaDefinitionType = undefined> extends events.EventEmitter {
/**
* Create a new schema
Expand Down Expand Up @@ -1167,7 +1169,7 @@ declare module 'mongoose' {
pre<T extends Model<DocType> = M>(method: 'insertMany' | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err: CallbackError) => void) => void): this;

/** Object of currently defined query helpers on this schema. */
query: { [name: string]: <T extends Query<any, any, any> = Query<any, any, any>>(this: T, ...args: any[]) => any };
query: { [name: string]: <T extends QueryWithHelpers<any, DocType, ExtractQueryHelpers<M>> = QueryWithHelpers<any, DocType, ExtractQueryHelpers<M>>>(this: T, ...args: any[]) => any };

/** Adds a method call to the queue. */
queue(name: string, args: any[]): this;
Expand Down Expand Up @@ -1852,7 +1854,7 @@ declare module 'mongoose' {
exec(callback?: (err: any, result: ResultType) => void): Promise<ResultType> | any;

// eslint-disable-next-line @typescript-eslint/ban-types
$where(argument: string | Function): Query<DocType[], DocType, THelpers>;
$where(argument: string | Function): QueryWithHelpers<DocType[], DocType, THelpers>;

/** Specifies an `$all` query condition. When called with one argument, the most recent path passed to `where()` is used. */
all(val: Array<any>): this;
Expand Down Expand Up @@ -1888,12 +1890,12 @@ declare module 'mongoose' {
comment(val: string): this;

/** Specifies this query as a `count` query. */
count(callback?: (err: any, count: number) => void): Query<number, DocType, THelpers>;
count(criteria: FilterQuery<DocType>, callback?: (err: any, count: number) => void): Query<number, DocType, THelpers>;
count(callback?: (err: any, count: number) => void): QueryWithHelpers<number, DocType, THelpers>;
count(criteria: FilterQuery<DocType>, callback?: (err: any, count: number) => void): QueryWithHelpers<number, DocType, THelpers>;

/** Specifies this query as a `countDocuments` query. */
countDocuments(callback?: (err: any, count: number) => void): Query<number, DocType, THelpers>;
countDocuments(criteria: FilterQuery<DocType>, callback?: (err: any, count: number) => void): Query<number, DocType, THelpers>;
countDocuments(callback?: (err: any, count: number) => void): QueryWithHelpers<number, DocType, THelpers>;
countDocuments(criteria: FilterQuery<DocType>, callback?: (err: any, count: number) => void): QueryWithHelpers<number, DocType, THelpers>;

/**
* Returns a wrapper around a [mongodb driver cursor](http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html).
Expand All @@ -1906,17 +1908,17 @@ declare module 'mongoose' {
* remove, except it deletes _every_ document that matches `filter` in the
* collection, regardless of the value of `single`.
*/
deleteMany(filter?: FilterQuery<DocType>, options?: QueryOptions, callback?: (err: CallbackError, res: any) => void): Query<any, DocType, THelpers>;
deleteMany(filter?: FilterQuery<DocType>, options?: QueryOptions, callback?: (err: CallbackError, res: any) => void): QueryWithHelpers<any, DocType, THelpers>;

/**
* Declare and/or execute this query as a `deleteOne()` operation. Works like
* remove, except it deletes at most one document regardless of the `single`
* option.
*/
deleteOne(filter?: FilterQuery<DocType>, options?: QueryOptions, callback?: (err: CallbackError, res: any) => void): Query<any, DocType, THelpers>;
deleteOne(filter?: FilterQuery<DocType>, options?: QueryOptions, callback?: (err: CallbackError, res: any) => void): QueryWithHelpers<any, DocType, THelpers>;

/** Creates a `distinct` query: returns the distinct values of the given `field` that match `filter`. */
distinct(field: string, filter?: FilterQuery<DocType>, callback?: (err: any, count: number) => void): Query<Array<any>, DocType, THelpers>;
distinct(field: string, filter?: FilterQuery<DocType>, callback?: (err: any, count: number) => void): QueryWithHelpers<Array<any>, DocType, THelpers>;

/** Specifies a `$elemMatch` query condition. When called with one argument, the most recent path passed to `where()` is used. */
// eslint-disable-next-line @typescript-eslint/ban-types
Expand All @@ -1935,7 +1937,7 @@ declare module 'mongoose' {
equals(val: any): this;

/** Creates a `estimatedDocumentCount` query: counts the number of documents in the collection. */
estimatedDocumentCount(options?: QueryOptions, callback?: (err: any, count: number) => void): Query<number, DocType, THelpers>;
estimatedDocumentCount(options?: QueryOptions, callback?: (err: any, count: number) => void): QueryWithHelpers<number, DocType, THelpers>;

/** Specifies a `$exists` query condition. When called with one argument, the most recent path passed to `where()` is used. */
exists(val: boolean): this;
Expand All @@ -1950,31 +1952,31 @@ declare module 'mongoose' {
explain(verbose?: string): this;

/** Creates a `find` query: gets a list of documents that match `filter`. */
find(callback?: (err: any, docs: DocType[]) => void): Query<Array<DocType>, DocType, THelpers>;
find(filter: FilterQuery<DocType>, callback?: (err: any, docs: DocType[]) => void): Query<Array<DocType>, DocType, THelpers>;
find(filter: FilterQuery<DocType>, projection?: any | null, options?: QueryOptions | null, callback?: (err: CallbackError, docs: DocType[]) => void): Query<Array<DocType>, DocType, THelpers>;
find(callback?: (err: any, docs: DocType[]) => void): QueryWithHelpers<Array<DocType>, DocType, THelpers>;
find(filter: FilterQuery<DocType>, callback?: (err: any, docs: DocType[]) => void): QueryWithHelpers<Array<DocType>, DocType, THelpers>;
find(filter: FilterQuery<DocType>, projection?: any | null, options?: QueryOptions | null, callback?: (err: CallbackError, docs: DocType[]) => void): QueryWithHelpers<Array<DocType>, DocType, THelpers>;

/** Declares the query a findOne operation. When executed, the first found document is passed to the callback. */
findOne(filter?: FilterQuery<DocType>, projection?: any | null, options?: QueryOptions | null, callback?: (err: CallbackError, doc: DocType | null) => void): Query<DocType | null, DocType, THelpers>;
findOne(filter?: FilterQuery<DocType>, projection?: any | null, options?: QueryOptions | null, callback?: (err: CallbackError, doc: DocType | null) => void): QueryWithHelpers<DocType | null, DocType, THelpers>;

/** Creates a `findOneAndDelete` query: atomically finds the given document, deletes it, and returns the document as it was before deletion. */
findOneAndDelete(filter?: FilterQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query<DocType | null, DocType, THelpers>;
findOneAndDelete(filter?: FilterQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): QueryWithHelpers<DocType | null, DocType, THelpers>;

/** Creates a `findOneAndRemove` query: atomically finds the given document and deletes it. */
findOneAndRemove(filter?: FilterQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query<DocType | null, DocType, THelpers>;
findOneAndRemove(filter?: FilterQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): QueryWithHelpers<DocType | null, DocType, THelpers>;

/** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */
findOneAndUpdate(filter: FilterQuery<DocType>, update: UpdateQuery<DocType>, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject<DocType>, res: any) => void): Query<mongodb.FindAndModifyWriteOpResultObject<DocType>, DocType, THelpers>;
findOneAndUpdate(filter: FilterQuery<DocType>, update: UpdateQuery<DocType>, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: DocType, res: any) => void): Query<DocType, DocType, THelpers>;
findOneAndUpdate(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query<DocType | null, DocType, THelpers>;
findOneAndUpdate(filter: FilterQuery<DocType>, update: UpdateQuery<DocType>, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject<DocType>, res: any) => void): QueryWithHelpers<mongodb.FindAndModifyWriteOpResultObject<DocType>, DocType, THelpers>;
findOneAndUpdate(filter: FilterQuery<DocType>, update: UpdateQuery<DocType>, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: DocType, res: any) => void): QueryWithHelpers<DocType, DocType, THelpers>;
findOneAndUpdate(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): QueryWithHelpers<DocType | null, DocType, THelpers>;

/** Creates a `findByIdAndDelete` query, filtering by the given `_id`. */
findByIdAndDelete(id?: mongodb.ObjectId | any, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query<DocType | null, DocType, THelpers>;
findByIdAndDelete(id?: mongodb.ObjectId | any, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): QueryWithHelpers<DocType | null, DocType, THelpers>;

/** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */
findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery<DocType>, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject<DocType>, res: any) => void): Query<mongodb.FindAndModifyWriteOpResultObject<DocType>, DocType, THelpers>;
findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery<DocType>, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: DocType, res: any) => void): Query<DocType, DocType, THelpers>;
findByIdAndUpdate(id?: mongodb.ObjectId | any, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query<DocType | null, DocType, THelpers>;
findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery<DocType>, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject<DocType>, res: any) => void): QueryWithHelpers<mongodb.FindAndModifyWriteOpResultObject<DocType>, DocType, THelpers>;
findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery<DocType>, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: DocType, res: any) => void): QueryWithHelpers<DocType, DocType, THelpers>;
findByIdAndUpdate(id?: mongodb.ObjectId | any, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): QueryWithHelpers<DocType | null, DocType, THelpers>;

/** Specifies a `$geometry` condition */
geometry(object: { type: string, coordinates: any[] }): this;
Expand Down Expand Up @@ -2023,7 +2025,7 @@ declare module 'mongoose' {
j(val: boolean | null): this;

/** Sets the lean option. */
lean<LeanResultType = LeanDocumentOrArray<ResultType>>(val?: boolean | any): Query<LeanResultType, DocType, THelpers>;
lean<LeanResultType = LeanDocumentOrArray<ResultType>>(val?: boolean | any): QueryWithHelpers<LeanResultType, DocType, THelpers>;

/** Specifies the maximum number of documents the query will return. */
limit(val: number): this;
Expand All @@ -2040,7 +2042,7 @@ declare module 'mongoose' {
* Runs a function `fn` and treats the return value of `fn` as the new value
* for the query to resolve to.
*/
map<MappedType>(fn: (doc: DocType) => MappedType): Query<MappedType, DocType, THelpers>;
map<MappedType>(fn: (doc: DocType) => MappedType): QueryWithHelpers<MappedType, DocType, THelpers>;

/** Specifies an `$maxDistance` query condition. When called with one argument, the most recent path passed to `where()` is used. */
maxDistance(val: number): this;
Expand Down Expand Up @@ -2092,7 +2094,7 @@ declare module 'mongoose' {
* This is handy for integrating with async/await, because `orFail()` saves you
* an extra `if` statement to check if no document was found.
*/
orFail(err?: NativeError | (() => NativeError)): Query<NonNullable<ResultType>, DocType, THelpers>;
orFail(err?: NativeError | (() => NativeError)): QueryWithHelpers<NonNullable<ResultType>, DocType, THelpers>;

/** Specifies a `$polygon` condition */
polygon(...coordinatePairs: number[][]): this;
Expand Down Expand Up @@ -2120,14 +2122,14 @@ declare module 'mongoose' {
* deprecated, you should use [`deleteOne()`](#query_Query-deleteOne)
* or [`deleteMany()`](#query_Query-deleteMany) instead.
*/
remove(filter?: FilterQuery<DocType>, callback?: (err: CallbackError, res: mongodb.WriteOpResult['result']) => void): Query<mongodb.WriteOpResult['result'], DocType, THelpers>;
remove(filter?: FilterQuery<DocType>, callback?: (err: CallbackError, res: mongodb.WriteOpResult['result']) => void): QueryWithHelpers<mongodb.WriteOpResult['result'], DocType, THelpers>;

/**
* Declare and/or execute this query as a replaceOne() operation. Same as
* `update()`, except MongoDB will replace the existing document and will
* not accept any [atomic](https://docs.mongodb.com/manual/tutorial/model-data-for-atomic-operations/#pattern) operators (`$set`, etc.)
*/
replaceOne(filter?: FilterQuery<DocType>, replacement?: DocumentDefinition<DocType>, options?: QueryOptions | null, callback?: (err: any, res: any) => void): Query<any, DocType, THelpers>;
replaceOne(filter?: FilterQuery<DocType>, replacement?: DocumentDefinition<DocType>, options?: QueryOptions | null, callback?: (err: any, res: any) => void): QueryWithHelpers<any, DocType, THelpers>;

/** Specifies which document fields to include or exclude (also known as the query "projection") */
select(arg: string | any): this;
Expand Down Expand Up @@ -2193,24 +2195,24 @@ declare module 'mongoose' {
then: Promise<ResultType>['then'];

/** Converts this query to a customized, reusable query constructor with all arguments and options retained. */
toConstructor(): new (...args: any[]) => Query<ResultType, DocType, THelpers>;
toConstructor(): new (...args: any[]) => QueryWithHelpers<ResultType, DocType, THelpers>;

/** Declare and/or execute this query as an update() operation. */
update(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: CallbackError, res: UpdateWriteOpResult) => void): Query<UpdateWriteOpResult, DocType, THelpers>;
update(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: CallbackError, res: UpdateWriteOpResult) => void): QueryWithHelpers<UpdateWriteOpResult, DocType, THelpers>;

/**
* Declare and/or execute this query as an updateMany() operation. Same as
* `update()`, except MongoDB will update _all_ documents that match
* `filter` (as opposed to just the first one) regardless of the value of
* the `multi` option.
*/
updateMany(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: CallbackError, res: UpdateWriteOpResult) => void): Query<UpdateWriteOpResult, DocType, THelpers>;
updateMany(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: CallbackError, res: UpdateWriteOpResult) => void): QueryWithHelpers<UpdateWriteOpResult, DocType, THelpers>;

/**
* Declare and/or execute this query as an updateOne() operation. Same as
* `update()`, except it does not support the `multi` or `overwrite` options.
*/
updateOne(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: CallbackError, res: UpdateWriteOpResult) => void): Query<UpdateWriteOpResult, DocType, THelpers>;
updateOne(filter?: FilterQuery<DocType>, update?: UpdateQuery<DocType>, options?: QueryOptions | null, callback?: (err: CallbackError, res: UpdateWriteOpResult) => void): QueryWithHelpers<UpdateWriteOpResult, DocType, THelpers>;

/**
* Sets the specified number of `mongod` servers, or tag set of `mongod` servers,
Expand Down
10 changes: 5 additions & 5 deletions test/typescript/queries.ts
@@ -1,16 +1,16 @@
import { Schema, model, Document, Types, Query, Model } from 'mongoose';
import { Schema, model, Document, Types, Query, Model, QueryWithHelpers } from 'mongoose';

interface QueryHelpers {
byName(name: string): Query<Array<ITest>, ITest, QueryHelpers>;
byName(name: string): QueryWithHelpers<Array<ITest>, ITest, QueryHelpers>;
}

const schema: Schema<ITest, Model<ITest>> = new Schema({
const schema: Schema<ITest, Model<ITest, QueryHelpers>> = new Schema({
name: { type: 'String' },
tags: [String],
docs: [{ nested: { id: Number } }]
});

schema.query.byName = function(name: string): Query<Array<ITest>, ITest, QueryHelpers> {
schema.query.byName = function(name: string): Query<any, ITest, QueryHelpers> & QueryHelpers {
return this.find({ name });
};

Expand All @@ -28,7 +28,7 @@ interface ITest extends Document {

const Test = model<ITest, Model<ITest, QueryHelpers>>('Test', schema);

Test.find().byName('test').orFail().exec().then(console.log);
Test.find().byName('test').byName('test2').orFail().exec().then(console.log);

Test.count({ name: /Test/ }).exec().then((res: number) => console.log(res));
Test.findOne({ 'docs.id': 42 }).exec().then(console.log);
Expand Down

0 comments on commit 96f7905

Please sign in to comment.