From 1dc6da74246c152a3d6567be6b7bc5a1d1f72f05 Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Tue, 16 Sep 2025 09:03:46 +0100 Subject: [PATCH 1/4] fix firestore types and add test --- package.json | 2 + packages/app/lib/modular/index.d.ts | 4 +- packages/firestore/lib/index.d.ts | 13 +- packages/firestore/lib/index.test-d.ts | 72 +++++++++++ packages/firestore/lib/modular/Bytes.d.ts | 4 +- packages/firestore/lib/modular/index.d.ts | 122 +++++++++---------- packages/firestore/lib/modular/index.js | 4 +- packages/firestore/lib/modular/query.d.ts | 90 +++++++------- packages/firestore/lib/modular/snapshot.d.ts | 63 +++++----- 9 files changed, 229 insertions(+), 145 deletions(-) create mode 100644 packages/firestore/lib/index.test-d.ts diff --git a/package.json b/package.json index 394d057f92..645ecd1cc0 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "lint:spellcheck": "spellchecker --quiet --files=\"docs/**/*.md\" --dictionaries=\"./.spellcheck.dict.txt\" --reports=\"spelling.json\" --plugins spell indefinite-article repeated-words syntax-mentions syntax-urls frontmatter", "tsc:compile": "tsc --project .", "lint:all": "yarn lint && yarn lint:markdown && yarn lint:spellcheck && yarn tsc:compile", + "tests:tsd": "npx tsd packages/firestore", "tests:ai:mocks": "yarn ts-node ./scripts/fetch_ai_mock_responses.ts && yarn ts-node ./packages/ai/__tests__/test-utils/convert-mocks.ts", "tests:jest": "jest", "tests:jest-watch": "jest --watch", @@ -100,6 +101,7 @@ "spellchecker-cli": "^7.0.0", "ts-jest": "^29.4.1", "ts-node": "^10.9.2", + "tsd": "^0.33.0", "typescript": "^5.9.2" }, "resolutions": { diff --git a/packages/app/lib/modular/index.d.ts b/packages/app/lib/modular/index.d.ts index 49a8337d82..dc838e9f42 100644 --- a/packages/app/lib/modular/index.d.ts +++ b/packages/app/lib/modular/index.d.ts @@ -128,4 +128,6 @@ export function preferencesSetString(key: string, value: string): Promise; * * This is required if you want to persist things like Auth sessions, Analytics device IDs, etc. */ -export function setReactNativeAsyncStorage(asyncStorage: ReactNativeAsyncStorage): void; +export function setReactNativeAsyncStorage( + asyncStorage: ReactNativeFirebase.ReactNativeAsyncStorage, +): void; diff --git a/packages/firestore/lib/index.d.ts b/packages/firestore/lib/index.d.ts index 2fe76bc3c5..7fc2522bc4 100644 --- a/packages/firestore/lib/index.d.ts +++ b/packages/firestore/lib/index.d.ts @@ -56,7 +56,7 @@ export namespace FirebaseFirestoreTypes { export type QueryFilterType = 'OR' | 'AND'; export interface QueryFieldFilterConstraint { - fieldPath: keyof T | FieldPath; + fieldPath: FieldPath; operator: WhereFilterOp; value: any; } @@ -77,7 +77,7 @@ export namespace FirebaseFirestoreTypes { * e.g. Filter('name', '==', 'Ada') */ ( - fieldPath: keyof T | FieldPath, + fieldPath: FieldPath | string, operator: WhereFilterOp, value: any, ): QueryFieldFilterConstraint; @@ -894,7 +894,7 @@ export namespace FirebaseFirestoreTypes { // eslint-disable-next-line @typescript-eslint/no-unused-vars export class AggregateField { /** A type string to uniquely identify instances of this class. */ - type = 'AggregateField'; + readonly type: 'AggregateField'; } /** @@ -923,14 +923,13 @@ export namespace FirebaseFirestoreTypes { */ export interface AggregateQuerySnapshot< AggregateSpecType extends AggregateSpec, - AppModelType = DocumentData, - DbModelType extends DocumentData = DocumentData, + T extends DocumentData = DocumentData, > { /** * The underlying query over which the aggregations recorded in this * `AggregateQuerySnapshot` were performed. */ - get query(): Query; + get query(): Query; /** * Returns the results of the aggregations performed over the underlying @@ -953,7 +952,7 @@ export namespace FirebaseFirestoreTypes { /** * The underlying query for this instance. */ - get query(): Query; + get query(): Query; /** * Executes the query and returns the results as a AggregateQuerySnapshot. diff --git a/packages/firestore/lib/index.test-d.ts b/packages/firestore/lib/index.test-d.ts new file mode 100644 index 0000000000..afad4f565d --- /dev/null +++ b/packages/firestore/lib/index.test-d.ts @@ -0,0 +1,72 @@ +import { expectType } from 'tsd'; +import firebase, { + collection, + doc, + FirebaseFirestoreTypes, + getDoc, + increment, + onSnapshot, + query, + serverTimestamp, + setDoc, + Timestamp, + updateDoc, + where, +} from '.'; + +export const tests = [ + async () => { + type DocShape = { name: string; age: number; createdAt: Timestamp }; + const usersCollection = collection(firebase(), 'collectionName'); + expectType>(usersCollection); + const userDoc = doc(usersCollection, 'doc-id'); + expectType>(userDoc); + const snapshot = await getDoc(userDoc); + expectType>(snapshot); + expectType(snapshot.data()); + + updateDoc(userDoc, { age: 20, createdAt: serverTimestamp() }); + updateDoc(userDoc, { age: increment(1), createdAt: serverTimestamp() }); + + setDoc(userDoc, { + name: 'hi', + age: 30, + createdAt: Timestamp.fromDate(new Date()), + }); + + const q = query(usersCollection, where('not-typed-intentionally', '==', 'CA')); + onSnapshot(q, s => { + for (const doc of s.docs) { + expectType(doc.data()); + } + }); + + onSnapshot(q, { + next: s => { + for (const doc of s.docs) { + expectType(doc.data()); + } + }, + error: e => { + expectType(e); + }, + }); + + usersCollection.where('age', '>', 18).onSnapshot(s => { + for (const doc of s.docs) { + expectType(doc.data()); + } + }); + + where('a', '!=', 3); + where('a', '<', 3); + where('a', '<=', 3); + where('a', '==', 3); + where('a', '>', 3); + where('a', '>=', 3); + where('a', 'array-contains', 3); + where('a', 'array-contains-any', 3); + where('a', 'in', 3); + where('a', 'not-in', 3); + }, +]; diff --git a/packages/firestore/lib/modular/Bytes.d.ts b/packages/firestore/lib/modular/Bytes.d.ts index 7a3c6e4258..c88c158dde 100644 --- a/packages/firestore/lib/modular/Bytes.d.ts +++ b/packages/firestore/lib/modular/Bytes.d.ts @@ -1,4 +1,6 @@ -export declare class Bytes extends FirestoreBlob { +import { FirebaseFirestoreTypes } from '..'; + +export declare class Bytes extends FirebaseFirestoreTypes.Blob { static fromBase64String(base64: string): Bytes; static fromUint8Array(array: Uint8Array): Bytes; diff --git a/packages/firestore/lib/modular/index.d.ts b/packages/firestore/lib/modular/index.d.ts index 0dff797a08..1e232467a7 100644 --- a/packages/firestore/lib/modular/index.d.ts +++ b/packages/firestore/lib/modular/index.d.ts @@ -78,7 +78,7 @@ export declare type NestedUpdateFields> = Unio */ export declare type UpdateData = T extends Primitive ? T - : T extends object + : T extends Record ? { [K in keyof T]?: UpdateData | FieldValue; } & NestedUpdateFields @@ -185,11 +185,11 @@ export function doc( * a document. * @returns The `DocumentReference` instance. */ -export declare function doc( - reference: CollectionReference, +export declare function doc( + reference: CollectionReference, path?: string, ...pathSegments: string[] -): DocumentReference; +): DocumentReference; /** * Gets a `DocumentReference` instance that refers to a document within @@ -203,14 +203,14 @@ export declare function doc( * a document. * @returns The `DocumentReference` instance. */ -export declare function doc( - reference: DocumentReference, +export declare function doc( + reference: DocumentReference, path: string, ...pathSegments: string[] -): DocumentReference; +): DocumentReference; -export function doc( - parent: Firestore | CollectionReference | DocumentReference, +export function doc( + parent: Firestore | CollectionReference | DocumentReference, path?: string, ...pathSegments: string[] ): DocumentReference; @@ -227,11 +227,11 @@ export function doc( * to a collection. * @returns The `CollectionReference` instance. */ -export function collection( +export function collection( firestore: Firestore, path: string, ...pathSegments: string[] -): CollectionReference; +): CollectionReference; /** * Gets a `CollectionReference` instance that refers to a subcollection of @@ -245,11 +245,11 @@ export function collection( * to a collection. * @returns The `CollectionReference` instance. */ -export declare function collection( - reference: CollectionReference, +export declare function collection( + reference: CollectionReference, path: string, ...pathSegments: string[] -): CollectionReference; +): CollectionReference; /** * Gets a `CollectionReference` instance that refers to a subcollection of @@ -263,11 +263,11 @@ export declare function collection( - reference: DocumentReference, +export declare function collection( + reference: DocumentReference, path: string, ...pathSegments: string[] -): CollectionReference; +): CollectionReference; /** * Gets a `CollectionReference` instance that refers to a subcollection of @@ -288,7 +288,7 @@ export function collection( ): CollectionReference; export function collection( - parent: Firestore | DocumentReference | CollectionReference, + parent: Firestore | DocumentReference | CollectionReference, path: string, ...pathSegments: string[] ): CollectionReference; @@ -296,17 +296,13 @@ export function collection( /** *Returns true if the provided references are equal. * - * @param left DocumentReference | CollectionReference A reference to compare. - * @param right DocumentReference | CollectionReference A reference to compare. + * @param left DocumentReference | CollectionReference A reference to compare. + * @param right DocumentReference | CollectionReference A reference to compare. * @return boolean true if the references point to the same location in the same Firestore database. */ -export declare function refEqual( - left: - | DocumentReference - | CollectionReference, - right: - | DocumentReference - | CollectionReference, +export declare function refEqual( + left: DocumentReference | CollectionReference, + right: DocumentReference | CollectionReference, ): boolean; /** @@ -320,10 +316,7 @@ export declare function refEqual * will be included. Cannot contain a slash. * @returns The created `Query`. */ -export function collectionGroup( - firestore: Firestore, - collectionId: string, -): Query; +export function collectionGroup(firestore: Firestore, collectionId: string): Query; /** * Writes to the document referred to by this `DocumentReference`. If the @@ -334,7 +327,10 @@ export function collectionGroup( * @returns A `Promise` resolved once the data has been successfully written * to the backend (note that it won't resolve while you're offline). */ -export function setDoc(reference: DocumentReference, data: WithFieldValue): Promise; +export function setDoc( + reference: DocumentReference, + data: WithFieldValue, +): Promise; /** * Writes to the document referred to by the specified `DocumentReference`. If @@ -347,18 +343,12 @@ export function setDoc(reference: DocumentReference, data: WithFieldValue< * @returns A Promise resolved once the data has been successfully written * to the backend (note that it won't resolve while you're offline). */ -export function setDoc( +export function setDoc( reference: DocumentReference, data: PartialWithFieldValue, options: FirebaseFirestoreTypes.SetOptions, ): Promise; -export function setDoc( - reference: DocumentReference, - data: PartialWithFieldValue, - options?: FirebaseFirestoreTypes.SetOptions, -): Promise; - /** * Updates fields in the document referred to by the specified * `DocumentReference`. The update will fail if applied to a document that does @@ -371,7 +361,10 @@ export function setDoc( * @returns A `Promise` resolved once the data has been successfully written * to the backend (note that it won't resolve while you're offline). */ -export function updateDoc(reference: DocumentReference, data: UpdateData): Promise; +export function updateDoc( + reference: DocumentReference, + data: UpdateData, +): Promise; /** * Updates fields in the document referred to by the specified * `DocumentReference` The update will fail if applied to a document that does @@ -387,8 +380,8 @@ export function updateDoc(reference: DocumentReference, data: UpdateData, +export function updateDoc( + reference: DocumentReference, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[] @@ -404,10 +397,10 @@ export function updateDoc( * newly created document after it has been written to the backend (Note that it * won't resolve while you're offline). */ -export declare function addDoc( - reference: CollectionReference, - data: WithFieldValue, -): Promise>; +export declare function addDoc( + reference: CollectionReference, + data: WithFieldValue, +): Promise>; /** * Re-enables use of the network for this {@link Firestore} instance after a prior @@ -492,7 +485,7 @@ export function waitForPendingWrites(firestore: Firestore): Promise; */ export function initializeFirestore( app: FirebaseApp, - settings: FirestoreSettings, + settings: FirebaseFirestoreTypes.Settings, databaseId?: string, ): Promise; @@ -553,13 +546,12 @@ export function runTransaction( * retrieved from `snapshot.data().count`, where `snapshot` is the * `AggregateQuerySnapshot` to which the returned Promise resolves. */ -export function getCountFromServer( - query: Query, +export function getCountFromServer( + query: Query, ): Promise< FirebaseFirestoreTypes.AggregateQuerySnapshot< { count: FirebaseFirestoreTypes.AggregateField }, - AppModelType, - DbModelType + T > >; @@ -570,6 +562,19 @@ interface AggregateSpec { [field: string]: AggregateFieldType; } +export type FirebaseSignInProvider = + | 'custom' + | 'email' + | 'password' + | 'phone' + | 'anonymous' + | 'google.com' + | 'facebook.com' + | 'github.com' + | 'twitter.com' + | 'microsoft.com' + | 'apple.com'; + interface FirebaseIdToken { // Always set to https://securetoken.google.com/PROJECT_ID iss: string; @@ -636,12 +641,11 @@ export type AggregateFieldType = export function getAggregateFromServer< AggregateSpecType extends AggregateSpec, - AppModelType, - DbModelType extends DocumentData, + T extends DocumentData, >( - query: Query, + query: Query, aggregateSpec: AggregateSpecType, -): Promise>; +): Promise>; /** * Create an AggregateField object that can be used to compute the sum of @@ -663,6 +667,7 @@ export function average(field: string | FieldPath): AggregateField; +export type AggregateType = 'count' | 'avg' | 'sum'; /** * Represents an aggregation that can be performed by Firestore. */ @@ -680,12 +685,7 @@ export class AggregateField { * @param _internalFieldPath Optionally specifies the field that is aggregated. * @internal */ - constructor( - aggregateType: AggregateType = 'count', - readonly _internalFieldPath?: InternalFieldPath, - ) { - this.aggregateType = aggregateType; - } + constructor(aggregateType?: AggregateType, _internalFieldPath?: FieldPath); } /** diff --git a/packages/firestore/lib/modular/index.js b/packages/firestore/lib/modular/index.js index 141bdc8404..b01cd81474 100644 --- a/packages/firestore/lib/modular/index.js +++ b/packages/firestore/lib/modular/index.js @@ -72,8 +72,8 @@ export function collection(parent, path, ...pathSegments) { } /** - * @param {DocumentReference | CollectionReference} left - * @param {DocumentReference | CollectionReference} right + * @param {DocumentReference | CollectionReference} left + * @param {DocumentReference | CollectionReference} right * @return boolean true if the two references are equal */ export function refEqual(left, right) { diff --git a/packages/firestore/lib/modular/query.d.ts b/packages/firestore/lib/modular/query.d.ts index c4c4a31cbe..aa36f14849 100644 --- a/packages/firestore/lib/modular/query.d.ts +++ b/packages/firestore/lib/modular/query.d.ts @@ -24,12 +24,12 @@ export type QueryConstraintType = * An `AppliableConstraint` is an abstraction of a constraint that can be applied * to a Firestore query. */ -export interface AppliableConstraint { +export abstract class AppliableConstraint { /** * Takes the provided {@link Query} and returns a copy of the {@link Query} with this * {@link AppliableConstraint} applied. */ - _apply(query: Query): Query; + _apply(query: Query): Query; } /** @@ -40,7 +40,7 @@ export interface AppliableConstraint { * can then be passed to {@link (query:1)} to create a new query instance that * also contains this `QueryConstraint`. */ -export interface QueryConstraint extends AppliableConstraint { +export abstract class QueryConstraint extends AppliableConstraint { /** The type of this query constraint */ readonly type: QueryConstraintType; @@ -48,27 +48,27 @@ export interface QueryConstraint extends AppliableConstraint { * Takes the provided {@link Query} and returns a copy of the {@link Query} with this * {@link AppliableConstraint} applied. */ - _apply(query: Query): Query; + _apply(query: Query): Query; } export class QueryOrderByConstraint extends QueryConstraint { - readonly type: QueryConstraintType = 'orderBy'; + readonly type: 'orderBy'; } export class QueryLimitConstraint extends QueryConstraint { - readonly type: QueryConstraintType = 'limit'; + readonly type: 'limit'; } export class QueryStartAtConstraint extends QueryConstraint { - readonly type: QueryConstraintType = 'startAt'; + readonly type: 'startAt'; } export class QueryEndAtConstraint extends QueryConstraint { - readonly type: QueryConstraintType = 'endAt'; + readonly type: 'endAt'; } export class QueryFieldFilterConstraint extends QueryConstraint { - readonly type: QueryConstraintType = 'where'; + readonly type: 'where'; } /** @@ -100,11 +100,11 @@ export type QueryNonFilterConstraint = * @throws if any of the provided query constraints cannot be combined with the * existing or new constraints. */ -export declare function query( - query: Query, +export declare function query( + query: Query, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[] -): Query; +): Query; /** * Creates a new immutable instance of {@link Query} that is extended to also @@ -116,15 +116,15 @@ export declare function query( * @throws if any of the provided query constraints cannot be combined with the * existing or new constraints. */ -export declare function query( - query: Query, +export declare function query( + query: Query, ...queryConstraints: QueryConstraint[] -): Query; +): Query; -export function query( +export function query( query: Query, - queryConstraint: QueryCompositeFilterConstraint | IQueryConstraint | undefined, - ...additionalQueryConstraints: Array + queryConstraint: QueryCompositeFilterConstraint | QueryConstraint | undefined, + ...additionalQueryConstraints: Array ): Query; /** @@ -144,6 +144,12 @@ export function where( value: unknown, ): QueryFieldFilterConstraint; +/** + * `QueryFilterConstraint` is a helper union type that represents + * {@link QueryFieldFilterConstraint} and {@link QueryCompositeFilterConstraint}. + */ +export type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint; + /** * The or() function used to generate a logical OR query. * e.g. or(where('name', '==', 'Ada'), where('name', '==', 'Bob')) @@ -176,7 +182,7 @@ export type OrderByDirection = 'desc' | 'asc'; */ export function orderBy( fieldPath: string | FieldPath, - directionStr?: OrderByDirection = 'asc', + directionStr?: OrderByDirection, ): QueryOrderByConstraint; /** @@ -188,7 +194,7 @@ export function orderBy( * @param snapshot - The snapshot of the document to start at. * @returns A {@link QueryStartAtConstraint} to pass to `query()`. */ -export function startAt(snapshot: DocumentSnapshot): QueryStartAtConstraint; +export function startAt(snapshot: DocumentSnapshot): QueryStartAtConstraint; /** * * Creates a {@link QueryStartAtConstraint} that modifies the result set to @@ -201,9 +207,7 @@ export function startAt(snapshot: DocumentSnapshot): QueryStartAtConstr */ export function startAt(...fieldValues: unknown[]): QueryStartAtConstraint; -export function startAt( - ...docOrFields: Array> -): QueryStartAtConstraint; +export function startAt(...docOrFields: Array): QueryStartAtConstraint; /** * Creates a {@link QueryStartAtConstraint} that modifies the result set to @@ -214,8 +218,8 @@ export function startAt( * @param snapshot - The snapshot of the document to start after. * @returns A {@link QueryStartAtConstraint} to pass to `query()` */ -export function startAfter( - snapshot: DocumentSnapshot, +export function startAfter( + snapshot: DocumentSnapshot, ): QueryStartAtConstraint; /** @@ -229,8 +233,8 @@ export function startAfter( */ export function startAfter(...fieldValues: unknown[]): QueryStartAtConstraint; -export function startAfter( - ...docOrFields: Array> +export function startAfter( + ...docOrFields: Array> ): QueryStartAtConstraint; /** @@ -254,7 +258,9 @@ export function limit(limit: number): QueryLimitConstraint; * @returns A Promise resolved with a `DocumentSnapshot` containing the * current document contents. */ -export declare function getDoc(reference: DocumentReference): Promise>; +export declare function getDoc( + reference: DocumentReference, +): Promise>; /** * Reads the document referred to by this `DocumentReference` from cache. @@ -263,7 +269,7 @@ export declare function getDoc(reference: DocumentReference): Promise( +export declare function getDocFromCache( reference: DocumentReference, ): Promise>; @@ -274,7 +280,7 @@ export declare function getDocFromCache( * @returns A `Promise` resolved with a `DocumentSnapshot` containing the * current document contents. */ -export declare function getDocFromServer( +export declare function getDocFromServer( reference: DocumentReference, ): Promise>; @@ -288,9 +294,7 @@ export declare function getDocFromServer( * * @returns A `Promise` that will be resolved with the results of the query. */ -export declare function getDocs( - query: Query, -): Promise>; +export declare function getDocs(query: Query): Promise>; /** * Executes the query and returns the results as a `QuerySnapshot` from cache. @@ -299,9 +303,9 @@ export declare function getDocs( * * @returns A `Promise` that will be resolved with the results of the query. */ -export declare function getDocsFromCache( - query: Query, -): Promise>; +export declare function getDocsFromCache( + query: Query, +): Promise>; /** * Executes the query and returns the results as a `QuerySnapshot` from the @@ -309,9 +313,9 @@ export declare function getDocsFromCache( - query: Query, -): Promise>; +export declare function getDocsFromServer( + query: Query, +): Promise>; /** * Deletes the document referred to by the specified `DocumentReference`. @@ -320,8 +324,8 @@ export declare function getDocsFromServer( - reference: DocumentReference, +export declare function deleteDoc( + reference: DocumentReference, ): Promise; /** @@ -337,9 +341,7 @@ export declare function endAt(...fieldValues: unknown[]): QueryEndAtConstraint; * The end position is relative to the order of the query. The document must contain all of the fields provided in the orderBy of the query. * @param snapshot */ -export function endAt( - snapshot: DocumentSnapshot, -): QueryEndAtConstraint; +export function endAt(snapshot: DocumentSnapshot): QueryEndAtConstraint; /** * Creates a QueryEndAtConstraint that modifies the result set to end before the provided fields relative to the order of the query. diff --git a/packages/firestore/lib/modular/snapshot.d.ts b/packages/firestore/lib/modular/snapshot.d.ts index ebf9279e75..0a52e7a241 100644 --- a/packages/firestore/lib/modular/snapshot.d.ts +++ b/packages/firestore/lib/modular/snapshot.d.ts @@ -1,5 +1,7 @@ -import { FirebaseFirestoreTypes } from '../index'; +import { FirebaseFirestoreTypes } from '..'; +import type Firestore from '..'; +import DocumentData = FirebaseFirestoreTypes.DocumentData; import DocumentReference = FirebaseFirestoreTypes.DocumentReference; import DocumentSnapshot = FirebaseFirestoreTypes.DocumentSnapshot; import SnapshotListenOptions = FirebaseFirestoreTypes.SnapshotListenOptions; @@ -22,7 +24,7 @@ export type FirestoreError = Error; * @returns An unsubscribe function that can be called to cancel * the snapshot listener. */ -export function onSnapshot( +export function onSnapshot( reference: DocumentReference, observer: { next?: (snapshot: DocumentSnapshot) => void; @@ -44,7 +46,7 @@ export function onSnapshot( * @returns An unsubscribe function that can be called to cancel * the snapshot listener. */ -export function onSnapshot( +export function onSnapshot( reference: DocumentReference, options: SnapshotListenOptions, observer: { @@ -71,7 +73,7 @@ export function onSnapshot( * @returns An unsubscribe function that can be called to cancel * the snapshot listener. */ -export function onSnapshot( +export function onSnapshot( reference: DocumentReference, onNext: (snapshot: DocumentSnapshot) => void, onError?: (error: FirestoreError) => void, @@ -96,7 +98,7 @@ export function onSnapshot( * @returns An unsubscribe function that can be called to cancel * the snapshot listener. */ -export function onSnapshot( +export function onSnapshot( reference: DocumentReference, options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot) => void, @@ -117,10 +119,10 @@ export function onSnapshot( * @returns An unsubscribe function that can be called to cancel * the snapshot listener. */ -export declare function onSnapshot( - query: Query, +export declare function onSnapshot( + query: Query, observer: { - next?: (snapshot: QuerySnapshot) => void; + next?: (snapshot: QuerySnapshot) => void; error?: (error: FirestoreError) => void; complete?: () => void; }, @@ -140,11 +142,11 @@ export declare function onSnapshot( - query: Query, +export declare function onSnapshot( + query: Query, options: SnapshotListenOptions, observer: { - next?: (snapshot: QuerySnapshot) => void; + next?: (snapshot: QuerySnapshot) => void; error?: (error: FirestoreError) => void; complete?: () => void; }, @@ -168,9 +170,9 @@ export declare function onSnapshot( - query: Query, - onNext: (snapshot: QuerySnapshot) => void, +export declare function onSnapshot( + query: Query, + onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, ): Unsubscribe; @@ -194,10 +196,10 @@ export declare function onSnapshot( - query: Query, +export declare function onSnapshot( + query: Query, options: SnapshotListenOptions, - onNext: (snapshot: QuerySnapshot) => void, + onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, ): Unsubscribe; @@ -205,27 +207,27 @@ export declare function onSnapshot | QuerySnapshot A snapshot to compare. - * @param right DocumentSnapshot | QuerySnapshot A snapshot to compare. + * @param left DocumentSnapshot | QuerySnapshot A snapshot to compare. + * @param right DocumentSnapshot | QuerySnapshot A snapshot to compare. * * @returns boolean true if the snapshots are equal. */ -export function snapshotEqual( - left: DocumentSnapshot | QuerySnapshot, - right: DocumentSnapshot | QuerySnapshot, +export function snapshotEqual( + left: DocumentSnapshot | QuerySnapshot, + right: DocumentSnapshot | QuerySnapshot, ): boolean; /** * Returns true if the provided queries point to the same collection and apply the same constraints. * - * @param left Query A Query to compare. - * @param right Query A Query to compare. + * @param left Query A Query to compare. + * @param right Query A Query to compare. * * @return boolean true if the references point to the same location in the same Firestore database. */ -export declare function queryEqual( - left: Query, - right: Query, +export declare function queryEqual( + left: Query, + right: Query, ): boolean; /** @@ -237,7 +239,7 @@ export declare function queryEqual void; error?: (error: FirestoreError) => void; @@ -253,4 +255,7 @@ export declare function onSnapshotsInSync( * @param firestore * @param onSync */ -export declare function onSnapshotsInSync(firestore: Firestore, onSync: () => void): Unsubscribe; +export declare function onSnapshotsInSync( + firestore: typeof Firestore, + onSync: () => void, +): Unsubscribe; From 2c047ca0e430b66b12c7c7baf0cad152d2099b4a Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Tue, 16 Sep 2025 09:15:45 +0100 Subject: [PATCH 2/4] add tsd workflow --- .github/workflows/tests_tsd.yml | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/tests_tsd.yml diff --git a/.github/workflows/tests_tsd.yml b/.github/workflows/tests_tsd.yml new file mode 100644 index 0000000000..4cb04291bc --- /dev/null +++ b/.github/workflows/tests_tsd.yml @@ -0,0 +1,58 @@ +name: Testing (tsd) + +on: + pull_request: + branches: + - '**' + paths-ignore: + - 'docs/**' + - 'website/**' + - '.spellcheck.dict.txt' + - '**/*.md' + push: + branches: + - main + paths-ignore: + - 'docs/**' + - 'website/**' + - '.spellcheck.dict.txt' + - '**/*.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + tsd: + name: tsd + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 50 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - uses: actions/cache/restore@v4 + name: Yarn Cache Restore + id: yarn-cache + with: + path: .yarn/cache + key: ${{ runner.os }}-yarn-v1-${{ hashFiles('yarn.lock') }} + restore-keys: ${{ runner.os }}-yarn-v1 + - name: Yarn Install + uses: nick-fields/retry@v3 + with: + timeout_minutes: 15 + retry_wait_seconds: 60 + max_attempts: 3 + command: yarn + - name: tsd + run: yarn tests:tsd + - uses: actions/cache/save@v4 + name: Yarn Cache Save + if: "${{ github.ref == 'refs/heads/main' }}" + with: + path: .yarn/cache + key: ${{ runner.os }}-yarn-v1-${{ hashFiles('yarn.lock') }} From 1f44399db1d2861dbfb725ee836211e7988412ca Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Tue, 16 Sep 2025 09:19:38 +0100 Subject: [PATCH 3/4] update yarn lock --- yarn.lock | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 927b78f88a..aea487e0dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6121,6 +6121,13 @@ __metadata: languageName: node linkType: hard +"@tsd/typescript@npm:^5.9.2": + version: 5.9.2 + resolution: "@tsd/typescript@npm:5.9.2" + checksum: 10/a7f18de0c4338165a3b279dba3591ca27fbd7d2ae8e41476e6cb6c1501408141e51c44fa41dfe44c36a084f9dfa03def5fdf25157d9a791ca23f90dc8eea761b + languageName: node + linkType: hard + "@tufjs/canonical-json@npm:2.0.0": version: 2.0.0 resolution: "@tufjs/canonical-json@npm:2.0.0" @@ -6213,6 +6220,23 @@ __metadata: languageName: node linkType: hard +"@types/eslint@npm:^7.2.13": + version: 7.29.0 + resolution: "@types/eslint@npm:7.29.0" + dependencies: + "@types/estree": "npm:*" + "@types/json-schema": "npm:*" + checksum: 10/43e2de0ed1f0290ef9143cc379ffacc1053f415a46ed2b781c1f22c0d6e94c0ece8a9a23339b0903e519637d3d0ea6a006e16ef8dfa72f2758c7ba5025bca960 + languageName: node + linkType: hard + +"@types/estree@npm:*": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10/25a4c16a6752538ffde2826c2cc0c6491d90e69cd6187bef4a006dd2c3c45469f049e643d7e516c515f21484dc3d48fd5c870be158a5beb72f5baf3dc43e4099 + languageName: node + linkType: hard + "@types/estree@npm:^1.0.6": version: 1.0.6 resolution: "@types/estree@npm:1.0.6" @@ -6272,7 +6296,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.6": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.6": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7 @@ -10755,6 +10779,22 @@ __metadata: languageName: node linkType: hard +"eslint-formatter-pretty@npm:^4.1.0": + version: 4.1.0 + resolution: "eslint-formatter-pretty@npm:4.1.0" + dependencies: + "@types/eslint": "npm:^7.2.13" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.1.0" + eslint-rule-docs: "npm:^1.1.5" + log-symbols: "npm:^4.0.0" + plur: "npm:^4.0.0" + string-width: "npm:^4.2.0" + supports-hyperlinks: "npm:^2.0.0" + checksum: 10/e8e0cd3843513fff32a70b036dd349fdab81d73b5e522f23685181c907a1faf2b2ebcae1688dc71d0fc026184011792f7e39b833d349df18fe2baea00d017901 + languageName: node + linkType: hard + "eslint-plugin-mocha@npm:^11.1.0": version: 11.1.0 resolution: "eslint-plugin-mocha@npm:11.1.0" @@ -10815,6 +10855,13 @@ __metadata: languageName: node linkType: hard +"eslint-rule-docs@npm:^1.1.5": + version: 1.1.235 + resolution: "eslint-rule-docs@npm:1.1.235" + checksum: 10/38af5ab724eb8108c7918826bc19f5e9902e39fc7fb018e9c6fe70f8a010fa3c3ea589a1527c53a68f2d41c4406db9195e042580a618a6d3027021abe5f4b014 + languageName: node + linkType: hard + "eslint-scope@npm:^8.4.0": version: 8.4.0 resolution: "eslint-scope@npm:8.4.0" @@ -13460,6 +13507,13 @@ __metadata: languageName: node linkType: hard +"irregular-plurals@npm:^3.2.0": + version: 3.5.0 + resolution: "irregular-plurals@npm:3.5.0" + checksum: 10/27f04e66402264b78251c03973dd4866aba58b851579b2f1870f3610494a163c20c5161a3eae8fdd49a61a5379ee611460a1781aadc891ce0203bcd7a52e4850 + languageName: node + linkType: hard + "is-absolute@npm:^1.0.0": version: 1.0.0 resolution: "is-absolute@npm:1.0.0" @@ -14395,7 +14449,7 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:>=29.4.3 < 30, jest-diff@npm:^29.4.1": +"jest-diff@npm:>=29.4.3 < 30, jest-diff@npm:^29.0.3, jest-diff@npm:^29.4.1": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" dependencies: @@ -16328,6 +16382,26 @@ __metadata: languageName: node linkType: hard +"meow@npm:^9.0.0": + version: 9.0.0 + resolution: "meow@npm:9.0.0" + dependencies: + "@types/minimist": "npm:^1.2.0" + camelcase-keys: "npm:^6.2.2" + decamelize: "npm:^1.2.0" + decamelize-keys: "npm:^1.1.0" + hard-rejection: "npm:^2.1.0" + minimist-options: "npm:4.1.0" + normalize-package-data: "npm:^3.0.0" + read-pkg-up: "npm:^7.0.1" + redent: "npm:^3.0.0" + trim-newlines: "npm:^3.0.0" + type-fest: "npm:^0.18.0" + yargs-parser: "npm:^20.2.3" + checksum: 10/3d0f199b9ccd81856a112f651290676f6816833626df53cee72b8e2c9acbd95beea4fa1f9fa729a553b5a0e74b18954f9fbc74c3ab837b3fc44e57de98f6c18f + languageName: node + linkType: hard + "merge-descriptors@npm:1.0.1": version: 1.0.1 resolution: "merge-descriptors@npm:1.0.1" @@ -19346,6 +19420,15 @@ __metadata: languageName: node linkType: hard +"plur@npm:^4.0.0": + version: 4.0.0 + resolution: "plur@npm:4.0.0" + dependencies: + irregular-plurals: "npm:^3.2.0" + checksum: 10/fea2e903efca67cc5c7a8952fca3db46ae8d9e9353373b406714977e601a5d3b628bcb043c3ad2126c6ff0e73d8020bf43af30a72dd087eff1ec240eb13b90e1 + languageName: node + linkType: hard + "pngjs@npm:^3.3.0": version: 3.4.0 resolution: "pngjs@npm:3.4.0" @@ -20154,6 +20237,7 @@ __metadata: spellchecker-cli: "npm:^7.0.0" ts-jest: "npm:^29.4.1" ts-node: "npm:^10.9.2" + tsd: "npm:^0.33.0" typescript: "npm:^5.9.2" languageName: unknown linkType: soft @@ -20388,7 +20472,7 @@ __metadata: languageName: node linkType: hard -"read-pkg-up@npm:^7.0.1": +"read-pkg-up@npm:^7.0.0, read-pkg-up@npm:^7.0.1": version: 7.0.1 resolution: "read-pkg-up@npm:7.0.1" dependencies: @@ -23166,6 +23250,23 @@ __metadata: languageName: node linkType: hard +"tsd@npm:^0.33.0": + version: 0.33.0 + resolution: "tsd@npm:0.33.0" + dependencies: + "@tsd/typescript": "npm:^5.9.2" + eslint-formatter-pretty: "npm:^4.1.0" + globby: "npm:^11.0.1" + jest-diff: "npm:^29.0.3" + meow: "npm:^9.0.0" + path-exists: "npm:^4.0.0" + read-pkg-up: "npm:^7.0.0" + bin: + tsd: dist/cli.js + checksum: 10/a093a3e67e42ea42265c066c07f2021346c84780fbc4338edc96da6f239ee5595c132d87ae05e218fc5f794a64f89c624b7f2b171bc4ded43183bf1fded6190f + languageName: node + linkType: hard + "tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.5.3": version: 2.6.3 resolution: "tslib@npm:2.6.3" From 6d8173e7639496d6386fe0fa92e00e47de890b71 Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Tue, 16 Sep 2025 09:44:59 +0100 Subject: [PATCH 4/4] add test-d.ts to tsconfig and npmignore --- packages/firestore/.npmignore | 1 + tsconfig.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/firestore/.npmignore b/packages/firestore/.npmignore index 28e083635e..4a551a742d 100644 --- a/packages/firestore/.npmignore +++ b/packages/firestore/.npmignore @@ -65,3 +65,4 @@ android/.settings .eslintignore type-test.ts __tests__ +*.test-d.ts diff --git a/tsconfig.json b/tsconfig.json index 4486da5720..d03c62386b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "include": ["packages/**/lib/*.d.ts", "packages/**/type-test.ts"], + "include": ["packages/**/lib/*.d.ts", "packages/**/type-test.ts", "packages/**/*.test-d.ts"], "compilerOptions": { "noEmit": true, "target": "es5",