From fc4cbfd72fcfd070623255118f8e709052c31bb0 Mon Sep 17 00:00:00 2001 From: Hossein Saniei Date: Tue, 1 Oct 2019 01:34:11 +0330 Subject: [PATCH] feat(mongodb): add better return types for insertX functions (#38522) --- types/mongodb/index.d.ts | 53 ++++++++++++++---------- types/mongodb/test/collection/insertX.ts | 37 ++++++++++++++++- 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/types/mongodb/index.d.ts b/types/mongodb/index.d.ts index 6f43aa22c9..6f372ba084 100644 --- a/types/mongodb/index.d.ts +++ b/types/mongodb/index.d.ts @@ -32,7 +32,7 @@ /// -import { Binary, ObjectID, Timestamp } from 'bson'; +import { Binary, ObjectId, Timestamp } from 'bson'; import { EventEmitter } from 'events'; import { Readable, Writable } from "stream"; import { checkServerIdentity } from "tls"; @@ -857,6 +857,15 @@ export interface FSyncOptions extends CommonOptions { type OptionalId = Omit & { _id?: any }; +type ExtractIdType = + TSchema extends { _id: infer U } // user has defined a type for _id + ? ({} extends U ? Exclude : U) // Exclude is used here to fix typescript 2 bug when TSchema is "any" + : ObjectId; // user has not defined _id on schema + +// this adds _id as required property +type WithId = + Omit & { _id: ExtractIdType }; + /** http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html */ export interface Collection { /** @@ -1005,19 +1014,19 @@ export interface Collection { initializeUnorderedBulkOp(options?: CommonOptions): UnorderedBulkOperation; /** http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#insertOne */ /** @deprecated Use insertOne, insertMany or bulkWrite */ - insert(docs: OptionalId, callback: MongoCallback): void; + insert(docs: OptionalId, callback: MongoCallback>>): void; /** @deprecated Use insertOne, insertMany or bulkWrite */ - insert(docs: OptionalId, options?: CollectionInsertOneOptions): Promise; + insert(docs: OptionalId, options?: CollectionInsertOneOptions): Promise>>; /** @deprecated Use insertOne, insertMany or bulkWrite */ - insert(docs: OptionalId, options: CollectionInsertOneOptions, callback: MongoCallback): void; + insert(docs: OptionalId, options: CollectionInsertOneOptions, callback: MongoCallback>>): void; /** http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#insertMany */ - insertMany(docs: Array>, callback: MongoCallback): void; - insertMany(docs: Array>, options?: CollectionInsertManyOptions): Promise; - insertMany(docs: Array>, options: CollectionInsertManyOptions, callback: MongoCallback): void; + insertMany(docs: Array>, callback: MongoCallback>>): void; + insertMany(docs: Array>, options?: CollectionInsertManyOptions): Promise>>; + insertMany(docs: Array>, options: CollectionInsertManyOptions, callback: MongoCallback>>): void; /** http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#insertOne */ - insertOne(docs: OptionalId, callback: MongoCallback): void; - insertOne(docs: OptionalId, options?: CollectionInsertOneOptions): Promise; - insertOne(docs: OptionalId, options: CollectionInsertOneOptions, callback: MongoCallback): void; + insertOne(docs: OptionalId, callback: MongoCallback>>): void; + insertOne(docs: OptionalId, options?: CollectionInsertOneOptions): Promise>>; + insertOne(docs: OptionalId, options: CollectionInsertOneOptions, callback: MongoCallback>>): void; /** http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#isCapped */ isCapped(options?: { session: ClientSession }): Promise; isCapped(callback: MongoCallback): void; @@ -1730,10 +1739,10 @@ export interface FindOneOptions { } /** http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~insertWriteOpResult */ -export interface InsertWriteOpResult { +export interface InsertWriteOpResult> { insertedCount: number; - ops: any[]; - insertedIds: { [key: number]: ObjectID }; + ops: TSchema[]; + insertedIds: { [key: number]: TSchema['_id'] }; connection: any; result: { ok: number, n: number }; } @@ -1751,10 +1760,10 @@ export interface CollectionInsertOneOptions extends CommonOptions { } /** http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~insertOneWriteOpResult */ -export interface InsertOneWriteOpResult { +export interface InsertOneWriteOpResult> { insertedCount: number; - ops: any[]; - insertedId: ObjectID; + ops: TSchema[]; + insertedId: TSchema['_id']; connection: any; result: { ok: number, n: number }; } @@ -1792,7 +1801,7 @@ export interface UpdateWriteOpResult { matchedCount: number; modifiedCount: number; upsertedCount: number; - upsertedId: { _id: ObjectID }; + upsertedId: { _id: ObjectId }; } /** https://github.com/mongodb/node-mongodb-native/blob/2.2/lib/collection.js#L957 */ @@ -2037,13 +2046,13 @@ export class CommandCursor extends Readable { export class GridFSBucket { constructor(db: Db, options?: GridFSBucketOptions); /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#delete */ - delete(id: ObjectID, callback?: GridFSBucketErrorCallback): void; + delete(id: ObjectId, callback?: GridFSBucketErrorCallback): void; /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#drop */ drop(callback?: GridFSBucketErrorCallback): void; /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#find */ find(filter?: object, options?: GridFSBucketFindOptions): Cursor; /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#openDownloadStream */ - openDownloadStream(id: ObjectID, options?: { start: number, end: number }): GridFSBucketReadStream; + openDownloadStream(id: ObjectId, options?: { start: number, end: number }): GridFSBucketReadStream; /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#openDownloadStreamByName */ openDownloadStreamByName(filename: string, options?: { revision: number, start: number, end: number }): GridFSBucketReadStream; /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#openUploadStream */ @@ -2051,7 +2060,7 @@ export class GridFSBucket { /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#openUploadStreamWithId */ openUploadStreamWithId(id: GridFSBucketWriteStreamId, filename: string, options?: GridFSBucketOpenUploadStreamOptions): GridFSBucketWriteStream; /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html#rename */ - rename(id: ObjectID, filename: string, callback?: GridFSBucketErrorCallback): void; + rename(id: ObjectId, filename: string, callback?: GridFSBucketErrorCallback): void; } /** http://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucket.html */ @@ -2087,7 +2096,7 @@ export interface GridFSBucketOpenUploadStreamOptions { /** https://mongodb.github.io/node-mongodb-native/3.1/api/GridFSBucketReadStream.html */ export class GridFSBucketReadStream extends Readable { - id: ObjectID; + id: ObjectId; constructor(chunks: Collection, files: Collection, readPreference: object, filter: object, options?: GridFSBucketReadStreamOptions); } @@ -2154,7 +2163,7 @@ export interface ChangeStreamOptions { startAfter?: object; } -type GridFSBucketWriteStreamId = string | number | object | ObjectID; +type GridFSBucketWriteStreamId = string | number | object | ObjectId; export interface LoggerOptions { /** diff --git a/types/mongodb/test/collection/insertX.ts b/types/mongodb/test/collection/insertX.ts index fe70ccc092..bb4f6e8f21 100644 --- a/types/mongodb/test/collection/insertX.ts +++ b/types/mongodb/test/collection/insertX.ts @@ -1,5 +1,6 @@ import { connect } from 'mongodb'; import { connectionString } from '../index'; +import { ObjectId } from 'bson'; // test collection.insertX functions async function run() { @@ -20,13 +21,45 @@ async function run() { numberField?: number; fruitTags: string[]; } + type TestModelWithId = TestModel & { _id: ObjectId; }; const collection = db.collection('testCollection'); - collection.insertOne({ + const result = await collection.insert({ stringField: 'hola', fruitTags: ['Strawberry'], }); - collection.insertMany([ + const resultOne = await collection.insertOne({ + stringField: 'hola', + fruitTags: ['Strawberry'], + }); + const resultMany = await collection.insertMany([ { stringField: 'hola', fruitTags: ['Apple', 'Lemon'] }, { stringField: 'hola', numberField: 1, fruitTags: [] }, ]); + + // test results type + // should add a _id field with ObjectId type if it does not exist on collection type + result.ops[0]._id; // $ExpectType ObjectId + resultMany.ops[0]._id; // $ExpectType ObjectId + resultOne.ops[0]._id; // $ExpectType ObjectId + result.insertedIds; // $ExpectType { [key: number]: ObjectId; } + resultMany.insertedIds; // $ExpectType { [key: number]: ObjectId; } + resultOne.insertedId; // $ExpectType ObjectId + + // should add a _id field with user specified type + type TestModelWithCustomId = TestModel & { _id: number; }; + const collectionWithId = db.collection('testCollection'); + + const resultOneWithId = await collectionWithId.insertOne({ + stringField: 'hola', + fruitTags: ['Strawberry'], + }); + const resultManyWithId = await collectionWithId.insertMany([ + { stringField: 'hola', fruitTags: ['Apple', 'Lemon'] }, + { stringField: 'hola', numberField: 1, fruitTags: [] }, + ]); + + resultOneWithId.ops[0]._id; // $ExpectType number + resultOneWithId.insertedId; // $ExpectType number + resultManyWithId.ops[0]._id; // $ExpectType number + resultManyWithId.insertedIds; // $ExpectType { [key: number]: number; } }