Skip to content

fix(NODE-5213): ChangeStream.tryNext() should return TChange type #3649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/change_stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ export class ChangeStream<
/**
* Try to get the next available document from the Change Stream's cursor or `null` if an empty batch is returned
*/
async tryNext(): Promise<Document | null> {
async tryNext(): Promise<TChange | null> {
this._setIsIterator();
// Change streams must resume indefinitely while each resume event succeeds.
// This loop continues until either a change event is received or until a resume attempt
Expand Down
54 changes: 38 additions & 16 deletions test/types/change_stream.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,18 @@ import type {
ChangeStreamInvalidateDocument,
ChangeStreamNameSpace,
ChangeStreamOptions,
ChangeStreamRefineCollectionShardKeyDocument,
ChangeStreamRenameDocument,
ChangeStreamReplaceDocument,
ChangeStreamReshardCollectionDocument,
ChangeStreamShardCollectionDocument,
ChangeStreamUpdateDocument,
Collection,
Document,
ResumeToken,
ServerSessionId,
Timestamp,
UpdateDescription
} from '../../src';
import type {
ChangeStreamRefineCollectionShardKeyDocument,
ChangeStreamReshardCollectionDocument,
ChangeStreamShardCollectionDocument
} from '../mongodb';

declare const changeStreamOptions: ChangeStreamOptions;
Expand Down Expand Up @@ -181,8 +179,8 @@ switch (change.operationType) {
// New fields can be added with $addFields, but you have to use TChange to type it
expectError(change.randomKeyAlwaysAccessibleBecauseOfPipelineFlexibilty);

declare const collection: Collection<Schema>;
const pipelineChangeStream = collection.watch<
declare const collectionWithSchema: Collection<Schema>;
const pipelineChangeStream = collectionWithSchema.watch<
Schema,
ChangeStreamInsertDocument<Schema> & { comment: string }
>([{ $addFields: { comment: 'big changes' } }, { $match: { operationType: 'insert' } }]);
Expand All @@ -193,28 +191,52 @@ pipelineChangeStream.on('change', change => {
expectType<Schema>(change.fullDocument);
});

collection.watch().on('change', change => expectType<ChangeStreamDocument<Schema>>(change));
collectionWithSchema
.watch()
.on('change', change => expectType<ChangeStreamDocument<Schema>>(change));

// Just overriding the schema provides a typed changestream OF that schema
collection
collectionWithSchema
.watch<Document>()
.on('change', change => expectType<ChangeStreamDocument<Document>>(change));

// both schema and Tchange can be made as flexible as possible (Document)
collection.watch<Document, Document>().on('change', change => expectType<Document>(change));
// both schema and TChange can be made as flexible as possible (Document)
collectionWithSchema
.watch<Document, Document>()
.on('change', change => expectType<Document>(change));

// first argument does not stop you from making second more generic
collection.watch<{ a: number }, Document>().on('change', change => expectType<Document>(change));
collectionWithSchema
.watch<{ a: number }, Document>()
.on('change', change => expectType<Document>(change));

// Arguments must be objects
expectError(collection.watch<Document, number>());
expectError(collection.watch<number, number>());
expectError(collectionWithSchema.watch<Document, number>());
expectError(collectionWithSchema.watch<number, number>());

// First argument no longer relates to second
collection
collectionWithSchema
.watch<{ a: number }, { b: boolean }>()
.on('change', change => expectType<{ b: boolean }>(change));

expectType<AsyncGenerator<ChangeStreamDocument<Schema>, void, void>>(
collection.watch()[Symbol.asyncIterator]()
collectionWithSchema.watch()[Symbol.asyncIterator]()
);

// Change type returned to user is equivalent across next/tryNext/on/once/addListener
const changeStream = collectionWithSchema.watch();
expectType<ChangeStreamDocument<Schema> | null>(await changeStream.tryNext());
expectType<ChangeStreamDocument<Schema>>(await changeStream.next());
changeStream.on('change', change => expectType<ChangeStreamDocument<Schema>>(change));
changeStream.once('change', change => expectType<ChangeStreamDocument<Schema>>(change));
changeStream.addListener('change', change => expectType<ChangeStreamDocument<Schema>>(change));

declare const noSchemaCollection: Collection;
const changeStreamNoSchema = noSchemaCollection.watch();
expectType<ChangeStreamDocument<Document> | null>(await changeStreamNoSchema.tryNext());
expectType<ChangeStreamDocument<Document>>(await changeStreamNoSchema.next());
changeStreamNoSchema.on('change', change => expectType<ChangeStreamDocument<Document>>(change));
changeStreamNoSchema.once('change', change => expectType<ChangeStreamDocument<Document>>(change));
changeStreamNoSchema.addListener('change', change =>
expectType<ChangeStreamDocument<Document>>(change)
);