diff --git a/packages/firebase/index.d.ts b/packages/firebase/index.d.ts
index 3b0b5aa8538..d3658115bb6 100644
--- a/packages/firebase/index.d.ts
+++ b/packages/firebase/index.d.ts
@@ -7080,13 +7080,73 @@ declare namespace firebase.firestore {
*
*
* debug
for the most verbose logging level, primarily for
- * dubugging.
+ * debugging.
* error
to log errors only.
* silent
to turn off logging.
*
*/
export function setLogLevel(logLevel: LogLevel): void;
+ /**
+ * Converter used by `withConverter()` to transform user objects of type T
+ * into Firestore data.
+ *
+ * Using the converter allows you to specify generic type arguments when
+ * storing and retrieving objects from Firestore.
+ *
+ * @example
+ * ```typescript
+ * class Post {
+ * constructor(readonly title: string, readonly author: string) {}
+ *
+ * toString(): string {
+ * return this.title + ', by ' + this.author;
+ * }
+ * }
+ *
+ * const postConverter = {
+ * toFirestore(post: Post): firebase.firestore.DocumentData {
+ * return {title: post.title, author: post.author};
+ * },
+ * fromFirestore(
+ * snapshot: firebase.firestore.QueryDocumentSnapshot,
+ * options: firebase.firestore.SnapshotOptions
+ * ): Post {
+ * const data = snapshot.data(options)!;
+ * return new Post(data.title, data.author);
+ * }
+ * };
+ *
+ * const postSnap = await firebase.firestore()
+ * .collection('posts')
+ * .withConverter(postConverter)
+ * .doc().get();
+ * const post = postSnap.data();
+ * if (post !== undefined) {
+ * post.title; // string
+ * post.toString(); // Should be defined
+ * post.someNonExistentProperty; // TS error
+ * }
+ * ```
+ */
+ export interface FirestoreDataConverter {
+ /**
+ * Called by the Firestore SDK to convert a custom model object of type T
+ * into a plain Javascript object (suitable for writing directly to the
+ * Firestore database).
+ */
+ toFirestore(modelObject: T): DocumentData;
+
+ /**
+ * Called by the Firestore SDK to convert Firestore data into an object of
+ * type T. You can access your data by calling: `snapshot.data(options)`.
+ *
+ * @param snapshot A QueryDocumentSnapshot containing your data and metadata.
+ * @param options The SnapshotOptions from the initial call to `data()`.
+ */
+ fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): T;
+ }
+
/**
* The Cloud Firestore service interface.
*
@@ -7133,7 +7193,7 @@ declare namespace firebase.firestore {
* @param collectionPath A slash-separated path to a collection.
* @return The `CollectionReference` instance.
*/
- collection(collectionPath: string): CollectionReference;
+ collection(collectionPath: string): CollectionReference;
/**
* Gets a `DocumentReference` instance that refers to the document at the
@@ -7142,7 +7202,7 @@ declare namespace firebase.firestore {
* @param documentPath A slash-separated path to a document.
* @return The `DocumentReference` instance.
*/
- doc(documentPath: string): DocumentReference;
+ doc(documentPath: string): DocumentReference;
/**
* Creates and returns a new Query that includes all documents in the
@@ -7154,7 +7214,7 @@ declare namespace firebase.firestore {
* will be included. Cannot contain a slash.
* @return The created Query.
*/
- collectionGroup(collectionId: string): Query;
+ collectionGroup(collectionId: string): Query;
/**
* Executes the given `updateFunction` and then attempts to commit the changes
@@ -7384,7 +7444,7 @@ declare namespace firebase.firestore {
* from 0 to 999,999,999 inclusive.
*/
constructor(seconds: number, nanoseconds: number);
-
+
/**
* Creates a new timestamp with the current date, with millisecond precision.
*
@@ -7504,7 +7564,7 @@ declare namespace firebase.firestore {
* @param documentRef A reference to the document to be read.
* @return A DocumentSnapshot for the read data.
*/
- get(documentRef: DocumentReference): Promise;
+ get(documentRef: DocumentReference): Promise>;
/**
* Writes to the document referred to by the provided `DocumentReference`.
@@ -7516,9 +7576,9 @@ declare namespace firebase.firestore {
* @param options An object to configure the set behavior.
* @return This `Transaction` instance. Used for chaining method calls.
*/
- set(
- documentRef: DocumentReference,
- data: DocumentData,
+ set(
+ documentRef: DocumentReference,
+ data: T,
options?: SetOptions
): Transaction;
@@ -7533,7 +7593,7 @@ declare namespace firebase.firestore {
* within the document.
* @return This `Transaction` instance. Used for chaining method calls.
*/
- update(documentRef: DocumentReference, data: UpdateData): Transaction;
+ update(documentRef: DocumentReference, data: UpdateData): Transaction;
/**
* Updates fields in the document referred to by the provided
@@ -7551,7 +7611,7 @@ declare namespace firebase.firestore {
* to the backend (Note that it won't resolve while you're offline).
*/
update(
- documentRef: DocumentReference,
+ documentRef: DocumentReference,
field: string | FieldPath,
value: any,
...moreFieldsAndValues: any[]
@@ -7563,7 +7623,7 @@ declare namespace firebase.firestore {
* @param documentRef A reference to the document to be deleted.
* @return This `Transaction` instance. Used for chaining method calls.
*/
- delete(documentRef: DocumentReference): Transaction;
+ delete(documentRef: DocumentReference): Transaction;
}
/**
@@ -7590,9 +7650,9 @@ declare namespace firebase.firestore {
* @param options An object to configure the set behavior.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
- set(
- documentRef: DocumentReference,
- data: DocumentData,
+ set(
+ documentRef: DocumentReference,
+ data: T,
options?: SetOptions
): WriteBatch;
@@ -7607,7 +7667,7 @@ declare namespace firebase.firestore {
* within the document.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
- update(documentRef: DocumentReference, data: UpdateData): WriteBatch;
+ update(documentRef: DocumentReference, data: UpdateData): WriteBatch;
/**
* Updates fields in the document referred to by this `DocumentReference`.
@@ -7624,7 +7684,7 @@ declare namespace firebase.firestore {
* to the backend (Note that it won't resolve while you're offline).
*/
update(
- documentRef: DocumentReference,
+ documentRef: DocumentReference,
field: string | FieldPath,
value: any,
...moreFieldsAndValues: any[]
@@ -7636,7 +7696,7 @@ declare namespace firebase.firestore {
* @param documentRef A reference to the document to be deleted.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
- delete(documentRef: DocumentReference): WriteBatch;
+ delete(documentRef: DocumentReference): WriteBatch;
/**
* Commits all of the writes in this write batch as a single atomic unit.
@@ -7722,7 +7782,7 @@ declare namespace firebase.firestore {
* the referenced location may or may not exist. A `DocumentReference` can
* also be used to create a `CollectionReference` to a subcollection.
*/
- export class DocumentReference {
+ export class DocumentReference {
private constructor();
/**
@@ -7739,7 +7799,7 @@ declare namespace firebase.firestore {
/**
* The Collection this `DocumentReference` belongs to.
*/
- readonly parent: CollectionReference;
+ readonly parent: CollectionReference;
/**
* A string representing the path of the referenced document (relative
@@ -7754,7 +7814,7 @@ declare namespace firebase.firestore {
* @param collectionPath A slash-separated path to a collection.
* @return The `CollectionReference` instance.
*/
- collection(collectionPath: string): CollectionReference;
+ collection(collectionPath: string): CollectionReference;
/**
* Returns true if this `DocumentReference` is equal to the provided one.
@@ -7762,7 +7822,7 @@ declare namespace firebase.firestore {
* @param other The `DocumentReference` to compare against.
* @return true if this `DocumentReference` is equal to the provided one.
*/
- isEqual(other: DocumentReference): boolean;
+ isEqual(other: DocumentReference): boolean;
/**
* Writes to the document referred to by this `DocumentReference`. If the
@@ -7774,7 +7834,7 @@ declare namespace firebase.firestore {
* @return A Promise resolved once the data has been successfully written
* to the backend (Note that it won't resolve while you're offline).
*/
- set(data: DocumentData, options?: SetOptions): Promise;
+ set(data: T, options?: SetOptions): Promise;
/**
* Updates fields in the document referred to by this `DocumentReference`.
@@ -7828,7 +7888,7 @@ declare namespace firebase.firestore {
* @return A Promise resolved with a DocumentSnapshot containing the
* current document contents.
*/
- get(options?: GetOptions): Promise;
+ get(options?: GetOptions): Promise>;
/**
* Attaches a listener for DocumentSnapshot events. You may either pass
@@ -7843,7 +7903,7 @@ declare namespace firebase.firestore {
* the snapshot listener.
*/
onSnapshot(observer: {
- next?: (snapshot: DocumentSnapshot) => void;
+ next?: (snapshot: DocumentSnapshot) => void;
error?: (error: FirestoreError) => void;
complete?: () => void;
}): () => void;
@@ -7863,7 +7923,7 @@ declare namespace firebase.firestore {
onSnapshot(
options: SnapshotListenOptions,
observer: {
- next?: (snapshot: DocumentSnapshot) => void;
+ next?: (snapshot: DocumentSnapshot) => void;
error?: (error: Error) => void;
complete?: () => void;
}
@@ -7884,7 +7944,7 @@ declare namespace firebase.firestore {
* the snapshot listener.
*/
onSnapshot(
- onNext: (snapshot: DocumentSnapshot) => void,
+ onNext: (snapshot: DocumentSnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
@@ -7906,10 +7966,24 @@ declare namespace firebase.firestore {
*/
onSnapshot(
options: SnapshotListenOptions,
- onNext: (snapshot: DocumentSnapshot) => void,
+ onNext: (snapshot: DocumentSnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
+
+ /**
+ * Applies a custom data converter to this DocumentReference, allowing you
+ * to use your own custom model objects with Firestore. When you call
+ * set(), get(), etc. on the returned DocumentReference instance, the
+ * provided converter will convert between Firestore data and your custom
+ * type U.
+ *
+ * @param converter Converts objects to and from Firestore.
+ * @return A DocumentReference that uses the provided converter.
+ */
+ withConverter(
+ converter: FirestoreDataConverter
+ ): DocumentReference;
}
/**
@@ -7977,7 +8051,7 @@ declare namespace firebase.firestore {
* access will return 'undefined'. You can use the `exists` property to
* explicitly verify a document's existence.
*/
- export class DocumentSnapshot {
+ export class DocumentSnapshot {
protected constructor();
/**
@@ -7988,7 +8062,7 @@ declare namespace firebase.firestore {
/**
* The `DocumentReference` for the document included in the `DocumentSnapshot`.
*/
- readonly ref: DocumentReference;
+ readonly ref: DocumentReference;
/**
* Property of the `DocumentSnapshot` that provides the document's ID.
*/
@@ -8013,7 +8087,7 @@ declare namespace firebase.firestore {
* @return An Object containing all fields in the document or 'undefined' if
* the document doesn't exist.
*/
- data(options?: SnapshotOptions): DocumentData | undefined;
+ data(options?: SnapshotOptions): T | undefined;
/**
* Retrieves the field specified by `fieldPath`. Returns `undefined` if the
@@ -8038,7 +8112,7 @@ declare namespace firebase.firestore {
* @param other The `DocumentSnapshot` to compare against.
* @return true if this `DocumentSnapshot` is equal to the provided one.
*/
- isEqual(other: DocumentSnapshot): boolean;
+ isEqual(other: DocumentSnapshot): boolean;
}
/**
@@ -8052,7 +8126,9 @@ declare namespace firebase.firestore {
* `exists` property will always be true and `data()` will never return
* 'undefined'.
*/
- export class QueryDocumentSnapshot extends DocumentSnapshot {
+ export class QueryDocumentSnapshot extends DocumentSnapshot<
+ T
+ > {
private constructor();
/**
@@ -8068,7 +8144,7 @@ declare namespace firebase.firestore {
* not yet been set to their final value).
* @return An Object containing all fields in the document.
*/
- data(options?: SnapshotOptions): DocumentData;
+ data(options?: SnapshotOptions): T;
}
/**
@@ -8095,7 +8171,7 @@ declare namespace firebase.firestore {
* A `Query` refers to a Query which you can read or listen to. You can also
* construct refined `Query` objects by adding filters and ordering.
*/
- export class Query {
+ export class Query {
protected constructor();
/**
@@ -8118,7 +8194,7 @@ declare namespace firebase.firestore {
fieldPath: string | FieldPath,
opStr: WhereFilterOp,
value: any
- ): Query;
+ ): Query;
/**
* Creates and returns a new Query that's additionally sorted by the
@@ -8132,7 +8208,7 @@ declare namespace firebase.firestore {
orderBy(
fieldPath: string | FieldPath,
directionStr?: OrderByDirection
- ): Query;
+ ): Query;
/**
* Creates and returns a new Query that only returns the first matching
@@ -8141,7 +8217,7 @@ declare namespace firebase.firestore {
* @param limit The maximum number of items to return.
* @return The created Query.
*/
- limit(limit: number): Query;
+ limit(limit: number): Query;
/**
* Creates and returns a new Query that only returns the last matching
@@ -8153,7 +8229,7 @@ declare namespace firebase.firestore {
* @param limit The maximum number of items to return.
* @return The created Query.
*/
- limitToLast(limit: number): Query;
+ limitToLast(limit: number): Query;
/**
* Creates and returns a new Query that starts at the provided document
@@ -8164,7 +8240,7 @@ declare namespace firebase.firestore {
* @param snapshot The snapshot of the document to start at.
* @return The created Query.
*/
- startAt(snapshot: DocumentSnapshot): Query;
+ startAt(snapshot: DocumentSnapshot): Query;
/**
* Creates and returns a new Query that starts at the provided fields
@@ -8175,7 +8251,7 @@ declare namespace firebase.firestore {
* of the query's order by.
* @return The created Query.
*/
- startAt(...fieldValues: any[]): Query;
+ startAt(...fieldValues: any[]): Query;
/**
* Creates and returns a new Query that starts after the provided document
@@ -8186,7 +8262,7 @@ declare namespace firebase.firestore {
* @param snapshot The snapshot of the document to start after.
* @return The created Query.
*/
- startAfter(snapshot: DocumentSnapshot): Query;
+ startAfter(snapshot: DocumentSnapshot): Query;
/**
* Creates and returns a new Query that starts after the provided fields
@@ -8197,7 +8273,7 @@ declare namespace firebase.firestore {
* of the query's order by.
* @return The created Query.
*/
- startAfter(...fieldValues: any[]): Query;
+ startAfter(...fieldValues: any[]): Query;
/**
* Creates and returns a new Query that ends before the provided document
@@ -8208,7 +8284,7 @@ declare namespace firebase.firestore {
* @param snapshot The snapshot of the document to end before.
* @return The created Query.
*/
- endBefore(snapshot: DocumentSnapshot): Query;
+ endBefore(snapshot: DocumentSnapshot): Query;
/**
* Creates and returns a new Query that ends before the provided fields
@@ -8219,7 +8295,7 @@ declare namespace firebase.firestore {
* of the query's order by.
* @return The created Query.
*/
- endBefore(...fieldValues: any[]): Query;
+ endBefore(...fieldValues: any[]): Query;
/**
* Creates and returns a new Query that ends at the provided document
@@ -8230,7 +8306,7 @@ declare namespace firebase.firestore {
* @param snapshot The snapshot of the document to end at.
* @return The created Query.
*/
- endAt(snapshot: DocumentSnapshot): Query;
+ endAt(snapshot: DocumentSnapshot): Query;
/**
* Creates and returns a new Query that ends at the provided fields
@@ -8241,7 +8317,7 @@ declare namespace firebase.firestore {
* of the query's order by.
* @return The created Query.
*/
- endAt(...fieldValues: any[]): Query;
+ endAt(...fieldValues: any[]): Query;
/**
* Returns true if this `Query` is equal to the provided one.
@@ -8249,7 +8325,7 @@ declare namespace firebase.firestore {
* @param other The `Query` to compare against.
* @return true if this `Query` is equal to the provided one.
*/
- isEqual(other: Query): boolean;
+ isEqual(other: Query): boolean;
/**
* Executes the query and returns the results as a `QuerySnapshot`.
@@ -8262,7 +8338,7 @@ declare namespace firebase.firestore {
* @param options An object to configure the get behavior.
* @return A Promise that will be resolved with the results of the Query.
*/
- get(options?: GetOptions): Promise;
+ get(options?: GetOptions): Promise>;
/**
* Attaches a listener for QuerySnapshot events. You may either pass
@@ -8278,7 +8354,7 @@ declare namespace firebase.firestore {
* the snapshot listener.
*/
onSnapshot(observer: {
- next?: (snapshot: QuerySnapshot) => void;
+ next?: (snapshot: QuerySnapshot) => void;
error?: (error: Error) => void;
complete?: () => void;
}): () => void;
@@ -8299,7 +8375,7 @@ declare namespace firebase.firestore {
onSnapshot(
options: SnapshotListenOptions,
observer: {
- next?: (snapshot: QuerySnapshot) => void;
+ next?: (snapshot: QuerySnapshot) => void;
error?: (error: Error) => void;
complete?: () => void;
}
@@ -8321,7 +8397,7 @@ declare namespace firebase.firestore {
* the snapshot listener.
*/
onSnapshot(
- onNext: (snapshot: QuerySnapshot) => void,
+ onNext: (snapshot: QuerySnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
@@ -8344,10 +8420,21 @@ declare namespace firebase.firestore {
*/
onSnapshot(
options: SnapshotListenOptions,
- onNext: (snapshot: QuerySnapshot) => void,
+ onNext: (snapshot: QuerySnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
+
+ /**
+ * Applies a custom data converter to this Query, allowing you to use your
+ * own custom model objects with Firestore. When you call get() on the
+ * returned Query, the provided converter will convert between Firestore
+ * data and your custom type U.
+ *
+ * @param converter Converts objects to and from Firestore.
+ * @return A Query that uses the provided converter.
+ */
+ withConverter(converter: FirestoreDataConverter): Query;
}
/**
@@ -8357,14 +8444,14 @@ declare namespace firebase.firestore {
* number of documents can be determined via the `empty` and `size`
* properties.
*/
- export class QuerySnapshot {
+ export class QuerySnapshot {
private constructor();
/**
* The query on which you called `get` or `onSnapshot` in order to get this
* `QuerySnapshot`.
*/
- readonly query: Query;
+ readonly query: Query;
/**
* Metadata about this snapshot, concerning its source and if it has local
* modifications.
@@ -8372,7 +8459,7 @@ declare namespace firebase.firestore {
readonly metadata: SnapshotMetadata;
/** An array of all the documents in the `QuerySnapshot`. */
- readonly docs: QueryDocumentSnapshot[];
+ readonly docs: Array>;
/** The number of documents in the `QuerySnapshot`. */
readonly size: number;
@@ -8388,7 +8475,7 @@ declare namespace firebase.firestore {
* changes (i.e. only `DocumentSnapshot.metadata` changed) should trigger
* snapshot events.
*/
- docChanges(options?: SnapshotListenOptions): DocumentChange[];
+ docChanges(options?: SnapshotListenOptions): Array>;
/**
* Enumerates all of the documents in the `QuerySnapshot`.
@@ -8398,7 +8485,7 @@ declare namespace firebase.firestore {
* @param thisArg The `this` binding for the callback.
*/
forEach(
- callback: (result: QueryDocumentSnapshot) => void,
+ callback: (result: QueryDocumentSnapshot) => void,
thisArg?: any
): void;
@@ -8408,7 +8495,7 @@ declare namespace firebase.firestore {
* @param other The `QuerySnapshot` to compare against.
* @return true if this `QuerySnapshot` is equal to the provided one.
*/
- isEqual(other: QuerySnapshot): boolean;
+ isEqual(other: QuerySnapshot): boolean;
}
/**
@@ -8420,12 +8507,12 @@ declare namespace firebase.firestore {
* A `DocumentChange` represents a change to the documents matching a query.
* It contains the document affected and the type of change that occurred.
*/
- export interface DocumentChange {
+ export interface DocumentChange {
/** The type of change ('added', 'modified', or 'removed'). */
readonly type: DocumentChangeType;
/** The document affected by this change. */
- readonly doc: QueryDocumentSnapshot;
+ readonly doc: QueryDocumentSnapshot;
/**
* The index of the changed document in the result set immediately prior to
@@ -8448,7 +8535,7 @@ declare namespace firebase.firestore {
* document references, and querying for documents (using the methods
* inherited from `Query`).
*/
- export class CollectionReference extends Query {
+ export class CollectionReference extends Query {
private constructor();
/** The collection's identifier. */
@@ -8458,7 +8545,7 @@ declare namespace firebase.firestore {
* A reference to the containing `DocumentReference` if this is a subcollection.
* If this isn't a subcollection, the reference is null.
*/
- readonly parent: DocumentReference | null;
+ readonly parent: DocumentReference | null;
/**
* A string representing the path of the referenced collection (relative
@@ -8474,7 +8561,7 @@ declare namespace firebase.firestore {
* @param documentPath A slash-separated path to a document.
* @return The `DocumentReference` instance.
*/
- doc(documentPath?: string): DocumentReference;
+ doc(documentPath?: string): DocumentReference;
/**
* Add a new document to this collection with the specified data, assigning
@@ -8484,7 +8571,7 @@ declare namespace firebase.firestore {
* @return A Promise resolved with a `DocumentReference` pointing to the
* newly created document after it has been written to the backend.
*/
- add(data: DocumentData): Promise;
+ add(data: T): Promise>;
/**
* Returns true if this `CollectionReference` is equal to the provided one.
@@ -8492,7 +8579,20 @@ declare namespace firebase.firestore {
* @param other The `CollectionReference` to compare against.
* @return true if this `CollectionReference` is equal to the provided one.
*/
- isEqual(other: CollectionReference): boolean;
+ isEqual(other: CollectionReference): boolean;
+
+ /**
+ * 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 and your custom type U.
+ *
+ * @param converter Converts objects to and from Firestore.
+ * @return A CollectionReference that uses the provided converter.
+ */
+ withConverter(
+ converter: FirestoreDataConverter
+ ): CollectionReference;
}
/**
diff --git a/packages/firestore-types/index.d.ts b/packages/firestore-types/index.d.ts
index f446fd7af5e..c9c494236ce 100644
--- a/packages/firestore-types/index.d.ts
+++ b/packages/firestore-types/index.d.ts
@@ -40,6 +40,12 @@ export type LogLevel = 'debug' | 'error' | 'silent';
export function setLogLevel(logLevel: LogLevel): void;
+export interface FirestoreDataConverter {
+ toFirestore(modelObject: T): DocumentData;
+
+ fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): T;
+}
+
export class FirebaseFirestore {
private constructor();
@@ -47,11 +53,11 @@ export class FirebaseFirestore {
enablePersistence(settings?: PersistenceSettings): Promise;
- collection(collectionPath: string): CollectionReference;
+ collection(collectionPath: string): CollectionReference;
- doc(documentPath: string): DocumentReference;
+ doc(documentPath: string): DocumentReference;
- collectionGroup(collectionId: string): Query;
+ collectionGroup(collectionId: string): Query;
runTransaction(
updateFunction: (transaction: Transaction) => Promise
@@ -126,43 +132,43 @@ export class Blob {
export class Transaction {
private constructor();
- get(documentRef: DocumentReference): Promise;
+ get(documentRef: DocumentReference): Promise>;
- set(
- documentRef: DocumentReference,
- data: DocumentData,
+ set(
+ documentRef: DocumentReference,
+ data: T,
options?: SetOptions
): Transaction;
- update(documentRef: DocumentReference, data: UpdateData): Transaction;
+ update(documentRef: DocumentReference, data: UpdateData): Transaction;
update(
- documentRef: DocumentReference,
+ documentRef: DocumentReference,
field: string | FieldPath,
value: any,
...moreFieldsAndValues: any[]
): Transaction;
- delete(documentRef: DocumentReference): Transaction;
+ delete(documentRef: DocumentReference): Transaction;
}
export class WriteBatch {
private constructor();
- set(
- documentRef: DocumentReference,
- data: DocumentData,
+ set(
+ documentRef: DocumentReference,
+ data: T,
options?: SetOptions
): WriteBatch;
- update(documentRef: DocumentReference, data: UpdateData): WriteBatch;
+ update(documentRef: DocumentReference, data: UpdateData): WriteBatch;
update(
- documentRef: DocumentReference,
+ documentRef: DocumentReference,
field: string | FieldPath,
value: any,
...moreFieldsAndValues: any[]
): WriteBatch;
- delete(documentRef: DocumentReference): WriteBatch;
+ delete(documentRef: DocumentReference): WriteBatch;
commit(): Promise;
}
@@ -180,19 +186,19 @@ export interface GetOptions {
readonly source?: 'default' | 'server' | 'cache';
}
-export class DocumentReference {
+export class DocumentReference {
private constructor();
readonly id: string;
readonly firestore: FirebaseFirestore;
- readonly parent: CollectionReference;
+ readonly parent: CollectionReference;
readonly path: string;
- collection(collectionPath: string): CollectionReference;
+ collection(collectionPath: string): CollectionReference;
- isEqual(other: DocumentReference): boolean;
+ isEqual(other: DocumentReference): boolean;
- set(data: DocumentData, options?: SetOptions): Promise;
+ set(data: T, options?: SetOptions): Promise;
update(data: UpdateData): Promise;
update(
@@ -203,39 +209,40 @@ export class DocumentReference {
delete(): Promise;
- get(options?: GetOptions): Promise;
+ get(options?: GetOptions): Promise>;
onSnapshot(observer: {
- next?: (snapshot: DocumentSnapshot) => void;
+ next?: (snapshot: DocumentSnapshot) => void;
error?: (error: FirestoreError) => void;
complete?: () => void;
}): () => void;
onSnapshot(
options: SnapshotListenOptions,
observer: {
- next?: (snapshot: DocumentSnapshot) => void;
+ next?: (snapshot: DocumentSnapshot) => void;
error?: (error: Error) => void;
complete?: () => void;
}
): () => void;
onSnapshot(
- onNext: (snapshot: DocumentSnapshot) => void,
+ onNext: (snapshot: DocumentSnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
onSnapshot(
options: SnapshotListenOptions,
- onNext: (snapshot: DocumentSnapshot) => void,
+ onNext: (snapshot: DocumentSnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
+
+ withConverter(converter: FirestoreDataConverter): DocumentReference;
}
export interface SnapshotOptions {
readonly serverTimestamps?: 'estimate' | 'previous' | 'none';
}
-/** Metadata about a snapshot, describing the state of the snapshot. */
export interface SnapshotMetadata {
readonly hasPendingWrites: boolean;
readonly fromCache: boolean;
@@ -243,24 +250,27 @@ export interface SnapshotMetadata {
isEqual(other: SnapshotMetadata): boolean;
}
-export class DocumentSnapshot {
+export class DocumentSnapshot {
protected constructor();
readonly exists: boolean;
- readonly ref: DocumentReference;
+ readonly ref: DocumentReference;
readonly id: string;
readonly metadata: SnapshotMetadata;
- data(options?: SnapshotOptions): DocumentData | undefined;
+ data(options?: SnapshotOptions): T | undefined;
get(fieldPath: string | FieldPath, options?: SnapshotOptions): any;
- isEqual(other: DocumentSnapshot): boolean;
+ isEqual(other: DocumentSnapshot): boolean;
}
-export class QueryDocumentSnapshot extends DocumentSnapshot {
+export class QueryDocumentSnapshot extends DocumentSnapshot<
+ T
+> {
private constructor();
- data(options?: SnapshotOptions): DocumentData;
+
+ data(options?: SnapshotOptions): T;
}
export type OrderByDirection = 'desc' | 'asc';
@@ -275,104 +285,114 @@ export type WhereFilterOp =
| 'in'
| 'array-contains-any';
-export class Query {
+export class Query {
protected constructor();
readonly firestore: FirebaseFirestore;
- where(fieldPath: string | FieldPath, opStr: WhereFilterOp, value: any): Query;
+ where(
+ fieldPath: string | FieldPath,
+ opStr: WhereFilterOp,
+ value: any
+ ): Query;
orderBy(
fieldPath: string | FieldPath,
directionStr?: OrderByDirection
- ): Query;
+ ): Query;
- limit(limit: number): Query;
+ limit(limit: number): Query;
- limitToLast(limit: number): Query;
+ limitToLast(limit: number): Query;
- startAt(snapshot: DocumentSnapshot): Query;
- startAt(...fieldValues: any[]): Query;
+ startAt(snapshot: DocumentSnapshot): Query;
+ startAt(...fieldValues: any[]): Query;
- startAfter(snapshot: DocumentSnapshot): Query;
- startAfter(...fieldValues: any[]): Query;
+ startAfter(snapshot: DocumentSnapshot): Query;
+ startAfter(...fieldValues: any[]): Query;
- endBefore(snapshot: DocumentSnapshot): Query;
- endBefore(...fieldValues: any[]): Query;
+ endBefore(snapshot: DocumentSnapshot): Query;
+ endBefore(...fieldValues: any[]): Query;
- endAt(snapshot: DocumentSnapshot): Query;
- endAt(...fieldValues: any[]): Query;
+ endAt(snapshot: DocumentSnapshot): Query;
+ endAt(...fieldValues: any[]): Query;
- isEqual(other: Query): boolean;
+ isEqual(other: Query): boolean;
- get(options?: GetOptions): Promise;
+ get(options?: GetOptions): Promise>;
onSnapshot(observer: {
- next?: (snapshot: QuerySnapshot) => void;
+ next?: (snapshot: QuerySnapshot) => void;
error?: (error: Error) => void;
complete?: () => void;
}): () => void;
onSnapshot(
options: SnapshotListenOptions,
observer: {
- next?: (snapshot: QuerySnapshot) => void;
+ next?: (snapshot: QuerySnapshot) => void;
error?: (error: Error) => void;
complete?: () => void;
}
): () => void;
onSnapshot(
- onNext: (snapshot: QuerySnapshot) => void,
+ onNext: (snapshot: QuerySnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
onSnapshot(
options: SnapshotListenOptions,
- onNext: (snapshot: QuerySnapshot) => void,
+ onNext: (snapshot: QuerySnapshot) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
+
+ withConverter(converter: FirestoreDataConverter): Query;
}
-export class QuerySnapshot {
+export class QuerySnapshot {
private constructor();
- readonly query: Query;
+ readonly query: Query;
readonly metadata: SnapshotMetadata;
- readonly docs: QueryDocumentSnapshot[];
+ readonly docs: Array>;
readonly size: number;
readonly empty: boolean;
- docChanges(options?: SnapshotListenOptions): DocumentChange[];
+ docChanges(options?: SnapshotListenOptions): Array>;
forEach(
- callback: (result: QueryDocumentSnapshot) => void,
+ callback: (result: QueryDocumentSnapshot) => void,
thisArg?: any
): void;
- isEqual(other: QuerySnapshot): boolean;
+ isEqual(other: QuerySnapshot): boolean;
}
export type DocumentChangeType = 'added' | 'removed' | 'modified';
-export interface DocumentChange {
+export interface DocumentChange {
readonly type: DocumentChangeType;
- readonly doc: QueryDocumentSnapshot;
+ readonly doc: QueryDocumentSnapshot;
readonly oldIndex: number;
readonly newIndex: number;
}
-export class CollectionReference extends Query {
+export class CollectionReference extends Query {
private constructor();
readonly id: string;
- readonly parent: DocumentReference | null;
+ readonly parent: DocumentReference | null;
readonly path: string;
- doc(documentPath?: string): DocumentReference;
+ doc(documentPath?: string): DocumentReference;
+
+ add(data: T): Promise>;
- add(data: DocumentData): Promise;
+ isEqual(other: CollectionReference): boolean;
- isEqual(other: CollectionReference): boolean;
+ withConverter(
+ converter: FirestoreDataConverter
+ ): CollectionReference;
}
export class FieldValue {
diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md
index 954c36d293a..dd419f3929d 100644
--- a/packages/firestore/CHANGELOG.md
+++ b/packages/firestore/CHANGELOG.md
@@ -1,4 +1,10 @@
-# Unreleased (1.8.0)
+# Unreleased
+- [feature] Added support for storing and retrieving custom types in Firestore.
+ Added support for strongly typed collections, documents, and
+ queries. You can now use `withConverter()` to supply a custom data
+ converter that will convert between Firestore data and your custom type.
+
+# 1.8.0
- [changed] Improved the performance of repeatedly executed queries when
persistence is enabled. Recently executed queries should see dramatic
improvements. This benefit is reduced if changes accumulate while the query
diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts
index 95d36cd3b68..99575a675ad 100644
--- a/packages/firestore/src/api/database.ts
+++ b/packages/firestore/src/api/database.ts
@@ -697,9 +697,9 @@ export class Transaction implements firestore.Transaction {
private _transaction: InternalTransaction
) {}
- get(
- documentRef: firestore.DocumentReference
- ): Promise {
+ get(
+ documentRef: firestore.DocumentReference
+ ): Promise> {
validateExactNumberOfArgs('Transaction.get', arguments, 1);
const ref = validateReference(
'Transaction.get',
@@ -714,20 +714,22 @@ export class Transaction implements firestore.Transaction {
}
const doc = docs[0];
if (doc instanceof NoDocument) {
- return new DocumentSnapshot(
+ return new DocumentSnapshot(
this._firestore,
ref._key,
null,
/* fromCache= */ false,
- /* hasPendingWrites= */ false
+ /* hasPendingWrites= */ false,
+ ref._converter
);
} else if (doc instanceof Document) {
- return new DocumentSnapshot(
+ return new DocumentSnapshot(
this._firestore,
ref._key,
doc,
/* fromCache= */ false,
- /* hasPendingWrites= */ false
+ /* hasPendingWrites= */ false,
+ ref._converter
);
} else {
throw fail(
@@ -737,9 +739,9 @@ export class Transaction implements firestore.Transaction {
});
}
- set(
- documentRef: firestore.DocumentReference,
- value: firestore.DocumentData,
+ set(
+ documentRef: firestore.DocumentReference,
+ value: T,
options?: firestore.SetOptions
): Transaction {
validateBetweenNumberOfArgs('Transaction.set', arguments, 2, 3);
@@ -749,30 +751,38 @@ export class Transaction implements firestore.Transaction {
this._firestore
);
options = validateSetOptions('Transaction.set', options);
+ const [convertedValue, functionName] = applyFirestoreDataConverter(
+ ref._converter,
+ value,
+ 'Transaction.set'
+ );
const parsed =
options.merge || options.mergeFields
? this._firestore._dataConverter.parseMergeData(
- 'Transaction.set',
- value,
+ functionName,
+ convertedValue,
options.mergeFields
)
- : this._firestore._dataConverter.parseSetData('Transaction.set', value);
+ : this._firestore._dataConverter.parseSetData(
+ functionName,
+ convertedValue
+ );
this._transaction.set(ref._key, parsed);
return this;
}
update(
- documentRef: firestore.DocumentReference,
+ documentRef: firestore.DocumentReference,
value: firestore.UpdateData
): Transaction;
update(
- documentRef: firestore.DocumentReference,
+ documentRef: firestore.DocumentReference,
field: string | ExternalFieldPath,
value: unknown,
...moreFieldsAndValues: unknown[]
): Transaction;
update(
- documentRef: firestore.DocumentReference,
+ documentRef: firestore.DocumentReference,
fieldOrUpdateData: string | ExternalFieldPath | firestore.UpdateData,
value?: unknown,
...moreFieldsAndValues: unknown[]
@@ -813,7 +823,7 @@ export class Transaction implements firestore.Transaction {
return this;
}
- delete(documentRef: firestore.DocumentReference): Transaction {
+ delete(documentRef: firestore.DocumentReference): Transaction {
validateExactNumberOfArgs('Transaction.delete', arguments, 1);
const ref = validateReference(
'Transaction.delete',
@@ -831,9 +841,9 @@ export class WriteBatch implements firestore.WriteBatch {
constructor(private _firestore: Firestore) {}
- set(
- documentRef: firestore.DocumentReference,
- value: firestore.DocumentData,
+ set(
+ documentRef: firestore.DocumentReference,
+ value: T,
options?: firestore.SetOptions
): WriteBatch {
validateBetweenNumberOfArgs('WriteBatch.set', arguments, 2, 3);
@@ -844,14 +854,22 @@ export class WriteBatch implements firestore.WriteBatch {
this._firestore
);
options = validateSetOptions('WriteBatch.set', options);
+ const [convertedValue, functionName] = applyFirestoreDataConverter(
+ ref._converter,
+ value,
+ 'WriteBatch.set'
+ );
const parsed =
options.merge || options.mergeFields
? this._firestore._dataConverter.parseMergeData(
- 'WriteBatch.set',
- value,
+ functionName,
+ convertedValue,
options.mergeFields
)
- : this._firestore._dataConverter.parseSetData('WriteBatch.set', value);
+ : this._firestore._dataConverter.parseSetData(
+ functionName,
+ convertedValue
+ );
this._mutations = this._mutations.concat(
parsed.toMutations(ref._key, Precondition.NONE)
);
@@ -859,17 +877,17 @@ export class WriteBatch implements firestore.WriteBatch {
}
update(
- documentRef: firestore.DocumentReference,
+ documentRef: firestore.DocumentReference,
value: firestore.UpdateData
): WriteBatch;
update(
- documentRef: firestore.DocumentReference,
+ documentRef: firestore.DocumentReference,
field: string | ExternalFieldPath,
value: unknown,
...moreFieldsAndValues: unknown[]
): WriteBatch;
update(
- documentRef: firestore.DocumentReference,
+ documentRef: firestore.DocumentReference,
fieldOrUpdateData: string | ExternalFieldPath | firestore.UpdateData,
value?: unknown,
...moreFieldsAndValues: unknown[]
@@ -914,7 +932,7 @@ export class WriteBatch implements firestore.WriteBatch {
return this;
}
- delete(documentRef: firestore.DocumentReference): WriteBatch {
+ delete(documentRef: firestore.DocumentReference): WriteBatch {
validateExactNumberOfArgs('WriteBatch.delete', arguments, 1);
this.verifyNotCommitted();
const ref = validateReference(
@@ -950,14 +968,23 @@ export class WriteBatch implements firestore.WriteBatch {
/**
* A reference to a particular document in a collection in the database.
*/
-export class DocumentReference implements firestore.DocumentReference {
+export class DocumentReference
+ implements firestore.DocumentReference {
private _firestoreClient: FirestoreClient;
- constructor(public _key: DocumentKey, readonly firestore: Firestore) {
+ constructor(
+ public _key: DocumentKey,
+ readonly firestore: Firestore,
+ readonly _converter?: firestore.FirestoreDataConverter
+ ) {
this._firestoreClient = this.firestore.ensureClientConfigured();
}
- static forPath(path: ResourcePath, firestore: Firestore): DocumentReference {
+ static forPath(
+ path: ResourcePath,
+ firestore: Firestore,
+ converter?: firestore.FirestoreDataConverter
+ ): DocumentReference {
if (path.length % 2 !== 0) {
throw new FirestoreError(
Code.INVALID_ARGUMENT,
@@ -966,22 +993,28 @@ export class DocumentReference implements firestore.DocumentReference {
`${path.canonicalString()} has ${path.length}`
);
}
- return new DocumentReference(new DocumentKey(path), firestore);
+ return new DocumentReference(new DocumentKey(path), firestore, converter);
}
get id(): string {
return this._key.path.lastSegment();
}
- get parent(): firestore.CollectionReference {
- return new CollectionReference(this._key.path.popLast(), this.firestore);
+ get parent(): firestore.CollectionReference {
+ return new CollectionReference(
+ this._key.path.popLast(),
+ this.firestore,
+ this._converter
+ );
}
get path(): string {
return this._key.path.canonicalString();
}
- collection(pathString: string): firestore.CollectionReference {
+ collection(
+ pathString: string
+ ): firestore.CollectionReference {
validateExactNumberOfArgs('DocumentReference.collection', arguments, 1);
validateArgType(
'DocumentReference.collection',
@@ -999,30 +1032,39 @@ export class DocumentReference implements firestore.DocumentReference {
return new CollectionReference(this._key.path.child(path), this.firestore);
}
- isEqual(other: firestore.DocumentReference): boolean {
+ isEqual(other: firestore.DocumentReference): boolean {
if (!(other instanceof DocumentReference)) {
throw invalidClassError('isEqual', 'DocumentReference', 1, other);
}
- return this.firestore === other.firestore && this._key.isEqual(other._key);
+ return (
+ this.firestore === other.firestore &&
+ this._key.isEqual(other._key) &&
+ this._converter === other._converter
+ );
}
set(
value: firestore.DocumentData,
options?: firestore.SetOptions
- ): Promise {
+ ): Promise;
+ set(value: T, options?: firestore.SetOptions): Promise {
validateBetweenNumberOfArgs('DocumentReference.set', arguments, 1, 2);
options = validateSetOptions('DocumentReference.set', options);
-
+ const [convertedValue, functionName] = applyFirestoreDataConverter(
+ this._converter,
+ value,
+ 'DocumentReference.set'
+ );
const parsed =
options.merge || options.mergeFields
? this.firestore._dataConverter.parseMergeData(
- 'DocumentReference.set',
- value,
+ functionName,
+ convertedValue,
options.mergeFields
)
: this.firestore._dataConverter.parseSetData(
- 'DocumentReference.set',
- value
+ functionName,
+ convertedValue
);
return this._firestoreClient.write(
parsed.toMutations(this._key, Precondition.NONE)
@@ -1074,20 +1116,20 @@ export class DocumentReference implements firestore.DocumentReference {
}
onSnapshot(
- observer: PartialObserver
+ observer: PartialObserver>
): Unsubscribe;
onSnapshot(
options: firestore.SnapshotListenOptions,
- observer: PartialObserver
+ observer: PartialObserver>
): Unsubscribe;
onSnapshot(
- onNext: NextFn,
+ onNext: NextFn>,
onError?: ErrorFn,
onCompletion?: CompleteFn
): Unsubscribe;
onSnapshot(
options: firestore.SnapshotListenOptions,
- onNext: NextFn,
+ onNext: NextFn>,
onError?: ErrorFn,
onCompletion?: CompleteFn
): Unsubscribe;
@@ -1102,7 +1144,7 @@ export class DocumentReference implements firestore.DocumentReference {
let options: firestore.SnapshotListenOptions = {
includeMetadataChanges: false
};
- let observer: PartialObserver;
+ let observer: PartialObserver>;
let currArg = 0;
if (
typeof args[currArg] === 'object' &&
@@ -1126,7 +1168,9 @@ export class DocumentReference implements firestore.DocumentReference {
};
if (isPartialObserver(args[currArg])) {
- observer = args[currArg] as PartialObserver;
+ observer = args[currArg] as PartialObserver<
+ firestore.DocumentSnapshot
+ >;
} else {
validateArgType(
'DocumentReference.onSnapshot',
@@ -1147,7 +1191,7 @@ export class DocumentReference implements firestore.DocumentReference {
args[currArg + 2]
);
observer = {
- next: args[currArg] as NextFn,
+ next: args[currArg] as NextFn>,
error: args[currArg + 1] as ErrorFn,
complete: args[currArg + 2] as CompleteFn
};
@@ -1157,7 +1201,7 @@ export class DocumentReference implements firestore.DocumentReference {
private onSnapshotInternal(
options: ListenOptions,
- observer: PartialObserver
+ observer: PartialObserver>
): Unsubscribe {
let errHandler = (err: Error): void => {
console.error('Uncaught Error in onSnapshot:', err);
@@ -1181,7 +1225,8 @@ export class DocumentReference implements firestore.DocumentReference {
this._key,
doc,
snapshot.fromCache,
- snapshot.hasPendingWrites
+ snapshot.hasPendingWrites,
+ this._converter
)
);
}
@@ -1200,11 +1245,11 @@ export class DocumentReference implements firestore.DocumentReference {
};
}
- get(options?: firestore.GetOptions): Promise {
+ get(options?: firestore.GetOptions): Promise> {
validateBetweenNumberOfArgs('DocumentReference.get', arguments, 0, 1);
validateGetOptions('DocumentReference.get', options);
return new Promise(
- (resolve: Resolver, reject: Rejecter) => {
+ (resolve: Resolver>, reject: Rejecter) => {
if (options && options.source === 'cache') {
this.firestore
.ensureClientConfigured()
@@ -1216,7 +1261,8 @@ export class DocumentReference implements firestore.DocumentReference {
this._key,
doc,
/*fromCache=*/ true,
- doc instanceof Document ? doc.hasLocalMutations : false
+ doc instanceof Document ? doc.hasLocalMutations : false,
+ this._converter
)
);
}, reject);
@@ -1228,7 +1274,7 @@ export class DocumentReference implements firestore.DocumentReference {
}
private getViaSnapshotListener(
- resolve: Resolver,
+ resolve: Resolver>,
reject: Rejecter,
options?: firestore.GetOptions
): void {
@@ -1238,7 +1284,7 @@ export class DocumentReference implements firestore.DocumentReference {
waitForSyncWhenOnline: true
},
{
- next: (snap: firestore.DocumentSnapshot) => {
+ next: (snap: firestore.DocumentSnapshot) => {
// Remove query first before passing event to user to avoid
// user actions affecting the now stale query.
unlisten();
@@ -1280,6 +1326,12 @@ export class DocumentReference implements firestore.DocumentReference {
}
);
}
+
+ withConverter(
+ converter: firestore.FirestoreDataConverter
+ ): firestore.DocumentReference {
+ return new DocumentReference(this._key, this.firestore, converter);
+ }
}
class SnapshotMetadata implements firestore.SnapshotMetadata {
@@ -1302,29 +1354,44 @@ class SnapshotMetadata implements firestore.SnapshotMetadata {
*/
export interface SnapshotOptions extends firestore.SnapshotOptions {}
-export class DocumentSnapshot implements firestore.DocumentSnapshot {
+export class DocumentSnapshot
+ implements firestore.DocumentSnapshot {
constructor(
private _firestore: Firestore,
private _key: DocumentKey,
public _document: Document | null,
private _fromCache: boolean,
- private _hasPendingWrites: boolean
+ private _hasPendingWrites: boolean,
+ private readonly _converter?: firestore.FirestoreDataConverter
) {}
- data(
- options?: firestore.SnapshotOptions
- ): firestore.DocumentData | undefined {
+ data(options?: firestore.SnapshotOptions): T | undefined {
validateBetweenNumberOfArgs('DocumentSnapshot.data', arguments, 0, 1);
options = validateSnapshotOptions('DocumentSnapshot.data', options);
- return !this._document
- ? undefined
- : this.convertObject(
+ if (!this._document) {
+ return undefined;
+ } else {
+ // We only want to use the converter and create a new DocumentSnapshot
+ // if a converter has been provided.
+ if (this._converter) {
+ const snapshot = new QueryDocumentSnapshot(
+ this._firestore,
+ this._key,
+ this._document,
+ this._fromCache,
+ this._hasPendingWrites
+ );
+ return this._converter.fromFirestore(snapshot, options);
+ } else {
+ return this.toJSObject(
this._document.data(),
FieldValueOptions.fromSnapshotOptions(
options,
this._firestore._areTimestampsInSnapshotsEnabled()
)
- );
+ ) as T;
+ }
+ }
}
get(
@@ -1338,7 +1405,7 @@ export class DocumentSnapshot implements firestore.DocumentSnapshot {
.data()
.field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));
if (value !== null) {
- return this.convertValue(
+ return this.toJSValue(
value,
FieldValueOptions.fromSnapshotOptions(
options,
@@ -1354,8 +1421,12 @@ export class DocumentSnapshot implements firestore.DocumentSnapshot {
return this._key.path.lastSegment();
}
- get ref(): firestore.DocumentReference {
- return new DocumentReference(this._key, this._firestore);
+ get ref(): firestore.DocumentReference {
+ return new DocumentReference(
+ this._key,
+ this._firestore,
+ this._converter
+ );
}
get exists(): boolean {
@@ -1366,7 +1437,7 @@ export class DocumentSnapshot implements firestore.DocumentSnapshot {
return new SnapshotMetadata(this._hasPendingWrites, this._fromCache);
}
- isEqual(other: firestore.DocumentSnapshot): boolean {
+ isEqual(other: firestore.DocumentSnapshot): boolean {
if (!(other instanceof DocumentSnapshot)) {
throw invalidClassError('isEqual', 'DocumentSnapshot', 1, other);
}
@@ -1376,26 +1447,27 @@ export class DocumentSnapshot implements firestore.DocumentSnapshot {
this._key.isEqual(other._key) &&
(this._document === null
? other._document === null
- : this._document.isEqual(other._document))
+ : this._document.isEqual(other._document)) &&
+ this._converter === other._converter
);
}
- private convertObject(
+ private toJSObject(
data: ObjectValue,
options: FieldValueOptions
): firestore.DocumentData {
const result: firestore.DocumentData = {};
data.forEach((key, value) => {
- result[key] = this.convertValue(value, options);
+ result[key] = this.toJSValue(value, options);
});
return result;
}
- private convertValue(value: FieldValue, options: FieldValueOptions): unknown {
+ private toJSValue(value: FieldValue, options: FieldValueOptions): unknown {
if (value instanceof ObjectValue) {
- return this.convertObject(value, options);
+ return this.toJSObject(value, options);
} else if (value instanceof ArrayValue) {
- return this.convertArray(value, options);
+ return this.toJSArray(value, options);
} else if (value instanceof RefValue) {
const key = value.value(options);
const database = this._firestore.ensureClientConfigured().databaseId();
@@ -1410,42 +1482,44 @@ export class DocumentSnapshot implements firestore.DocumentSnapshot {
`instead.`
);
}
- return new DocumentReference(key, this._firestore);
+ return new DocumentReference(key, this._firestore, this._converter);
} else {
return value.value(options);
}
}
- private convertArray(
- data: ArrayValue,
- options: FieldValueOptions
- ): unknown[] {
+ private toJSArray(data: ArrayValue, options: FieldValueOptions): unknown[] {
return data.internalValue.map(value => {
- return this.convertValue(value, options);
+ return this.toJSValue(value, options);
});
}
}
-export class QueryDocumentSnapshot extends DocumentSnapshot
- implements firestore.QueryDocumentSnapshot {
- data(options?: SnapshotOptions): firestore.DocumentData {
+export class QueryDocumentSnapshot
+ extends DocumentSnapshot
+ implements firestore.QueryDocumentSnapshot {
+ data(options?: SnapshotOptions): T {
const data = super.data(options);
assert(
- typeof data === 'object',
+ data !== undefined,
'Document in a QueryDocumentSnapshot should exist'
);
return data;
}
}
-export class Query implements firestore.Query {
- constructor(public _query: InternalQuery, readonly firestore: Firestore) {}
+export class Query implements firestore.Query {
+ constructor(
+ public _query: InternalQuery,
+ readonly firestore: Firestore,
+ protected readonly _converter?: firestore.FirestoreDataConverter
+ ) {}
where(
field: string | ExternalFieldPath,
opStr: firestore.WhereFilterOp,
value: unknown
- ): firestore.Query {
+ ): firestore.Query {
validateExactNumberOfArgs('Query.where', arguments, 3);
validateDefined('Query.where', 3, value);
@@ -1501,13 +1575,17 @@ export class Query implements firestore.Query {
}
const filter = FieldFilter.create(fieldPath, operator, fieldValue);
this.validateNewFilter(filter);
- return new Query(this._query.addFilter(filter), this.firestore);
+ return new Query(
+ this._query.addFilter(filter),
+ this.firestore,
+ this._converter
+ );
}
orderBy(
field: string | ExternalFieldPath,
directionStr?: firestore.OrderByDirection
- ): firestore.Query {
+ ): firestore.Query {
validateBetweenNumberOfArgs('Query.orderBy', arguments, 1, 2);
validateOptionalArgType(
'Query.orderBy',
@@ -1544,27 +1622,39 @@ export class Query implements firestore.Query {
const fieldPath = fieldPathFromArgument('Query.orderBy', field);
const orderBy = new OrderBy(fieldPath, direction);
this.validateNewOrderBy(orderBy);
- return new Query(this._query.addOrderBy(orderBy), this.firestore);
+ return new Query(
+ this._query.addOrderBy(orderBy),
+ this.firestore,
+ this._converter
+ );
}
- limit(n: number): firestore.Query {
+ limit(n: number): firestore.Query {
validateExactNumberOfArgs('Query.limit', arguments, 1);
validateArgType('Query.limit', 'number', 1, n);
validatePositiveNumber('Query.limit', 1, n);
- return new Query(this._query.withLimitToFirst(n), this.firestore);
+ return new Query(
+ this._query.withLimitToFirst(n),
+ this.firestore,
+ this._converter
+ );
}
- limitToLast(n: number): firestore.Query {
+ limitToLast(n: number): firestore.Query {
validateExactNumberOfArgs('Query.limitToLast', arguments, 1);
validateArgType('Query.limitToLast', 'number', 1, n);
validatePositiveNumber('Query.limitToLast', 1, n);
- return new Query(this._query.withLimitToLast(n), this.firestore);
+ return new Query(
+ this._query.withLimitToLast(n),
+ this.firestore,
+ this._converter
+ );
}
startAt(
- docOrField: unknown | firestore.DocumentSnapshot,
+ docOrField: unknown | firestore.DocumentSnapshot,
...fields: unknown[]
- ): firestore.Query {
+ ): firestore.Query {
validateAtLeastNumberOfArgs('Query.startAt', arguments, 1);
const bound = this.boundFromDocOrFields(
'Query.startAt',
@@ -1572,13 +1662,17 @@ export class Query implements firestore.Query {
fields,
/*before=*/ true
);
- return new Query(this._query.withStartAt(bound), this.firestore);
+ return new Query(
+ this._query.withStartAt(bound),
+ this.firestore,
+ this._converter
+ );
}
startAfter(
- docOrField: unknown | firestore.DocumentSnapshot,
+ docOrField: unknown | firestore.DocumentSnapshot,
...fields: unknown[]
- ): firestore.Query {
+ ): firestore.Query {
validateAtLeastNumberOfArgs('Query.startAfter', arguments, 1);
const bound = this.boundFromDocOrFields(
'Query.startAfter',
@@ -1586,13 +1680,17 @@ export class Query implements firestore.Query {
fields,
/*before=*/ false
);
- return new Query(this._query.withStartAt(bound), this.firestore);
+ return new Query(
+ this._query.withStartAt(bound),
+ this.firestore,
+ this._converter
+ );
}
endBefore(
- docOrField: unknown | firestore.DocumentSnapshot,
+ docOrField: unknown | firestore.DocumentSnapshot,
...fields: unknown[]
- ): firestore.Query {
+ ): firestore.Query {
validateAtLeastNumberOfArgs('Query.endBefore', arguments, 1);
const bound = this.boundFromDocOrFields(
'Query.endBefore',
@@ -1600,13 +1698,17 @@ export class Query implements firestore.Query {
fields,
/*before=*/ true
);
- return new Query(this._query.withEndAt(bound), this.firestore);
+ return new Query(
+ this._query.withEndAt(bound),
+ this.firestore,
+ this._converter
+ );
}
endAt(
- docOrField: unknown | firestore.DocumentSnapshot,
+ docOrField: unknown | firestore.DocumentSnapshot,
...fields: unknown[]
- ): firestore.Query {
+ ): firestore.Query