diff --git a/packages/firestore/lib/index.d.ts b/packages/firestore/lib/index.d.ts index 2fe76bc3c5..8fb96e3f19 100644 --- a/packages/firestore/lib/index.d.ts +++ b/packages/firestore/lib/index.d.ts @@ -141,6 +141,105 @@ 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); + + /** + * 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`). @@ -194,6 +293,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; } /** 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;