Firestore: Fix the updateDoc() incorrect parameter type issue by dconeybe · Pull Request #7310 · firebase/firebase-js-sdk (original) (raw)
Add a 2nd type parameter to FirestoreDataConverter to specify the shape of the data stored in Firestore so that it can be different than the high-level object that is used to represent the data in the rest of the application. This fixes a long-standing typing issue with updateDoc(), Transaction.update(), and WriteBatch.update() where erroneous TypeScript compilation errors would occur if the shape of the data written to Firestore was different from the shape of the T type parameter of the FirestoreDataConverter.
Background Information
The DocumentReference, CollectionReference, and Query classes in Firestore all provide a withConverter() method that associates a FirestoreDataConverter with the object. Once registered, any operations that read data from or write data to Firestore via the object will go through the FirestoreDataConverter. The primary use case of the converter is to transparently convert between a high-level application object, that is convenient for use in the application, and a low-level plain-old-data JavaScript object, that is actually stored in Firestore. These two objects do not necessarily have the same properties (although they can). So the FirestoreDataConverter converts objects in both directions.
Previously, the FirestoreDataConverter only had a single type parameter, T, which indicated the "high-level application object". This object type would then be specified to functions like setDoc(), addDoc(), Transaction.set() and WriteBatch.set() and the object would be converted to the database representation via the FirestoreDataConverter before writing it to Firestore. Similarly, the snapshot returned from functions like getDoc() and Transaction.get() would use the FirestoreDataConverter to convert from the raw data read from Firestore to the high-level object.
The problem was that the updateDoc() function was parameterized to also accept an object of type T. This worked as long as the properties of T were exactly those written to Firestore; however, since updateDoc() works on the raw data written to Firestore, if those properties were different then an incorrect TypeScript compiler error would occur. Customers often worked around this compilation error by casting the data argument to the "update" methods to any, basically turning off the type checks.
The Fix
The fix in this PR is to separate the specification of the high-level application object type and the low-level plain-old-data type that is actually stored in Firestore. It adds a 2nd type parameter to FirestoreDataConverter to specify this type and updateDoc(), Transaction.update() and WriteBatch.update() have their type parameter changed to accept this low-level type instead of the high-level type. TypeScript now produces accurate compilation errors, and also highlights that the "update" family of methods work on the low-level type rather than the high-level type.
Updating Your Code
If your application is written in pure JavaScript (rather than TypeScript) then you will not need to change anything. The changes in this PR are purely in the TypeScript typing system. However, if your application uses TypeScript, you may experience some new TypeScript compilation errors. You may need to update the argument types and return types of your implementations of FirestoreDataConverter. You may also want to create an interface for the low-level database type and specify it as the 2nd type parameter for your FirestoreDataConverter.
Finally, if you have functions that forward Firestore types like DocumentReference you may need to add a 2nd type parameter to forward it as well. For example, suppose you have a function function foo<T>(documentRef: DocumentReference<T>) then you may want to change it to function foo<AppModelType, DbModelType extends DocumentData>(documentRef: DocumentReference<AppModelType, DbModelType>) so that the arguments are perfectly forwarded.
Googlers see b/256607394 and go/firestore-updatedata-type-fix-api-proposal for details.