From f087fb40a0e4e8d5b1340447e10bc74be88d43b2 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Sat, 23 Aug 2025 13:18:50 -0400 Subject: [PATCH 1/3] adding withConverter call --- packages/firestore/lib/index.d.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/firestore/lib/index.d.ts b/packages/firestore/lib/index.d.ts index 2fe76bc3c5..3cc8071c5a 100644 --- a/packages/firestore/lib/index.d.ts +++ b/packages/firestore/lib/index.d.ts @@ -194,6 +194,22 @@ export namespace FirebaseFirestoreTypes { * @param documentPath A slash-separated path to a document. */ doc(documentPath?: string): DocumentReference; + + /** + * Applies a custom data converter to this CollectionReference, allowing you + * to use your own custom model objects with Firestore. When you call add() + * on the returned CollectionReference instance, the provided converter will + * convert between Firestore data of type `NewDbModelType` and your custom + * type `NewAppModelType`. + * + * @param converter Converts objects to and from Firestore. Passing in + * `null` removes the current converter. + * @return A CollectionReference that uses the provided converter. + */ + withConverter( + converter: FirestoreDataConverter, + ): CollectionReference; + withConverter(converter: null): CollectionReference; } /** From 7ef53d3d6def6e903d386338901c6d81e8ff8e59 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Sat, 23 Aug 2025 13:34:21 -0400 Subject: [PATCH 2/3] Moving WithFieldValues --- packages/firestore/lib/index.d.ts | 24 +++++++++++++++++++++ packages/firestore/lib/modular/index.d.ts | 26 ++--------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/packages/firestore/lib/index.d.ts b/packages/firestore/lib/index.d.ts index 3cc8071c5a..bd0e420415 100644 --- a/packages/firestore/lib/index.d.ts +++ b/packages/firestore/lib/index.d.ts @@ -141,6 +141,30 @@ export namespace FirebaseFirestoreTypes { [key: string]: any; } + /** + * Allows FieldValues to be passed in as a property value while maintaining + * type safety. + */ + export type WithFieldValue = + | T + | (T extends Primitive + ? T + : T extends object + ? { [K in keyof T]: WithFieldValue | FieldValue } + : never); + + /** + * Similar to Typescript's `Partial`, but allows nested fields to be + * omitted and FieldValues to be passed in as property values. + */ + export type PartialWithFieldValue = + | Partial + | (T extends Primitive + ? T + : T extends object + ? { [K in keyof T]?: PartialWithFieldValue | FieldValue } + : never); + /** * A `CollectionReference` object can be used for adding documents, getting document references, and querying for * documents (using the methods inherited from `Query`). diff --git a/packages/firestore/lib/modular/index.d.ts b/packages/firestore/lib/modular/index.d.ts index 0dff797a08..44d169b747 100644 --- a/packages/firestore/lib/modular/index.d.ts +++ b/packages/firestore/lib/modular/index.d.ts @@ -9,24 +9,14 @@ import DocumentData = FirebaseFirestoreTypes.DocumentData; import Query = FirebaseFirestoreTypes.Query; import FieldValue = FirebaseFirestoreTypes.FieldValue; import FieldPath = FirebaseFirestoreTypes.FieldPath; +import PartialWithFieldValue = FirebaseFirestoreTypes.PartialWithFieldValue; +import WithFieldValue = FirebaseFirestoreTypes.WithFieldValue; import PersistentCacheIndexManager = FirebaseFirestoreTypes.PersistentCacheIndexManager; import AggregateQuerySnapshot = FirebaseFirestoreTypes.AggregateQuerySnapshot; /** Primitive types. */ export type Primitive = string | number | boolean | undefined | null; -/** - * Similar to Typescript's `Partial`, but allows nested fields to be - * omitted and FieldValues to be passed in as property values. - */ -export type PartialWithFieldValue = - | Partial - | (T extends Primitive - ? T - : T extends object - ? { [K in keyof T]?: PartialWithFieldValue | FieldValue } - : never); - /** * Given a union type `U = T1 | T2 | ...`, returns an intersected type (`T1 & T2 & ...`). * @@ -84,18 +74,6 @@ export declare type UpdateData = T extends Primitive } & NestedUpdateFields : Partial; -/** - * Allows FieldValues to be passed in as a property value while maintaining - * type safety. - */ -export type WithFieldValue = - | T - | (T extends Primitive - ? T - : T extends object - ? { [K in keyof T]: WithFieldValue | FieldValue } - : never); - export type EmulatorMockTokenOptions = ({ user_id: string } | { sub: string }) & Partial; From 370ee6591d60380912a75de65be022b55e2ede19 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Sat, 23 Aug 2025 13:36:03 -0400 Subject: [PATCH 3/3] Created a FirestoreDataConverter interface --- packages/firestore/lib/index.d.ts | 75 +++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/packages/firestore/lib/index.d.ts b/packages/firestore/lib/index.d.ts index bd0e420415..8fb96e3f19 100644 --- a/packages/firestore/lib/index.d.ts +++ b/packages/firestore/lib/index.d.ts @@ -165,6 +165,81 @@ export namespace FirebaseFirestoreTypes { ? { [K in keyof T]?: PartialWithFieldValue | FieldValue } : never); + /** + * Converter used by `withConverter()` to transform user objects of type + * `AppModelType` into Firestore data of type `DbModelType`. + * + * Using the converter allows you to specify generic type arguments when + * storing and retrieving objects from Firestore. + * + * In this context, an "AppModel" is a class that is used in an application to + * package together related information and functionality. Such a class could, + * for example, have properties with complex, nested data types, properties + * used for memoization, properties of types not supported by Firestore (such + * as `symbol` and `bigint`), and helper functions that perform compound + * operations. Such classes are not suitable and/or possible to store into a + * Firestore database. Instead, instances of such classes need to be converted + * to "plain old JavaScript objects" (POJOs) with exclusively primitive + * properties, potentially nested inside other POJOs or arrays of POJOs. In + * this context, this type is referred to as the "DbModel" and would be an + * object suitable for persisting into Firestore. For convenience, + * applications can implement `FirestoreDataConverter` and register the + * converter with Firestore objects, such as `DocumentReference` or `Query`, + * to automatically convert `AppModel` to `DbModel` when storing into + * Firestore, and convert `DbModel` to `AppModel` when retrieving from + * Firestore. + */ + export interface FirestoreDataConverter< + AppModelType, + DbModelType extends DocumentData = DocumentData, + > { + /** + * Converts a custom model object of type + * `AppModelType` into a plain Javascript object (suitable for writing + * directly to the Firestore database) of type `DbModelType`. + * + * To use set() with `merge` and `mergeFields`, + * toFirestore() must be defined with `Partial`. + * + * The `WithFieldValue` type extends `T` to also allow FieldValues such + * as `FieldValue.delete()` to be used as property values. + */ + toFirestore(modelObject: WithFieldValue): WithFieldValue; + + /** + * Converts a custom model object of type + * `AppModelType` into a plain Javascript object (suitable for writing + * directly to the Firestore database) of type `DbModelType`. + * + * To use set() with `merge` and `mergeFields`, + * toFirestore() must be defined with `Partial`. + * + * The `PartialWithFieldValue` type extends `Partial` to allow + * FieldValues such as `FieldValue.delete()` to be used as property values. + * It also supports nested `Partial` by allowing nested fields to be + * omitted. + */ + toFirestore( + modelObject: PartialWithFieldValue, + options: SetOptions, + ): PartialWithFieldValue; + + /** + * Called by the Firestore SDK to convert Firestore data into an object of + * type `AppModelType`. You can access your data by calling: + * `snapshot.data()`. + * + * Generally, the data returned from `snapshot.data()` can be cast to + * `DbModelType`; however, this is not guaranteed because Firestore does not + * enforce a schema on the database. For example, writes from a previous + * version of the application or writes from another client that did not use + * a type converter could have written data with different properties and/or + * property types. The implementation will need to choose whether to + * gracefully recover from non-conforming data or throw an error. + */ + fromFirestore(snapshot: QueryDocumentSnapshot): AppModelType; + } + /** * A `CollectionReference` object can be used for adding documents, getting document references, and querying for * documents (using the methods inherited from `Query`).