@@ -101,149 +101,159 @@ toFirestore(modelObject: PartialWithFieldValue<AppModelType>, options: SetOption
101
101
Simple Example
102
102
103
103
` ` ` typescript
104
- const numberConverter = {
105
- toFirestore(value: WithFieldValue<number>) {
106
- return { value };
107
- },
108
- fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
109
- return snapshot.data(options).value as number;
110
- }
104
+ const numberConverter: FirestoreDataConverter<number, {value: number}> = {
105
+ toFirestore(value: WithFieldValue<number>) {
106
+ return { value };
107
+ },
108
+ fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
109
+ return snapshot.data(options).value as number;
110
+ }
111
111
};
112
112
113
113
async function simpleDemo(db: Firestore): Promise<void> {
114
- const documentRef = doc(db, 'values/value123').withConverter(numberConverter);
114
+ const documentRef = doc(db, 'values/value123').withConverter(numberConverter);
115
115
116
- await setDoc(documentRef, 42);
117
- const snapshot1 = await getDoc(documentRef);
118
- assertEqual(snapshot1.data(), 42);
116
+ // converters are used with ` setDoc ` , ` addDoc ` , and ` getDoc `
117
+ await setDoc(documentRef, 42);
118
+ const snapshot1 = await getDoc(documentRef);
119
+ assertEqual(snapshot1.data(), 42);
119
120
120
- await updateDoc(documentRef, { value: 999 });
121
- const snapshot2 = await getDoc(documentRef);
122
- assertEqual(snapshot2.data(), 999);
121
+ // converters are not used when writing data with ` updateDoc `
122
+ await updateDoc(documentRef, { value: 999 });
123
+ const snapshot2 = await getDoc(documentRef);
124
+ assertEqual(snapshot2.data(), 999);
123
125
}
124
126
125
127
` ` `
126
128
Advanced Example
127
129
128
130
` ` ` typescript
131
+ // The Post class is a model that is used by our application.
132
+ // This class may have properties and methods that are specific
133
+ // to our application execution, which do not need to be persisted
134
+ // to Firestore.
129
135
class Post {
130
- constructor(
131
- readonly title: string,
132
- readonly author: string,
133
- readonly lastUpdatedMillis: number
134
- ) {}
135
-
136
- toString(): string {
137
- return ` $ {this .title } by $ {this .author }` ;
138
- }
136
+ constructor(
137
+ readonly title: string,
138
+ readonly author: string,
139
+ readonly lastUpdatedMillis: number
140
+ ) {}
141
+ toString(): string {
142
+ return ` $ {this .title } by $ {this .author }` ;
143
+ }
139
144
}
140
145
146
+ // The PostDbModel represents how we want our posts to be stored
147
+ // in Firestore. This DbModel has different properties ( ` ttl ` ,
148
+ // ` aut ` , and ` lut ` ) from the Post class we use in our application.
141
149
interface PostDbModel {
142
- ttl: string;
143
- aut: { firstName: string; lastName: string };
144
- lut: Timestamp;
150
+ ttl: string;
151
+ aut: { firstName: string; lastName: string };
152
+ lut: Timestamp;
145
153
}
146
154
147
- const postConverter = {
148
- toFirestore(post: WithFieldValue<Post>) {
149
- return {
150
- ttl: post.title,
151
- aut: this._autFromAuthor(post.author),
152
- lut: this._lutFromLastUpdatedMillis(post.lastUpdatedMillis)
153
- };
154
- },
155
-
156
- fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
157
- const data = snapshot.data(options) as PostDbModel;
158
- const author = ` $ {data .aut .firstName } $ {data .aut .lastName }` ;
159
- return new Post(data.ttl, author, data.lut.toMillis());
160
- },
161
-
162
- _autFromAuthor(
163
- author: string | FieldValue
164
- ): { firstName: string; lastName: string } | FieldValue {
165
- if (typeof author !== 'string') {
166
- // ` author ` is a FieldValue, so just return it.
167
- return author;
155
+ // The ` PostConverter ` implements ` FirestoreDataConverter <AppModelType , DbModelType >`
156
+ // and specifies how the Firestore SDK can convert ` Post ` objects to ` PostDbModel `
157
+ // objects and vice versa.
158
+ class PostConverter implements FirestoreDataConverter<Post, PostDbModel> {
159
+ toFirestore(post: WithFieldValue<Post>) {
160
+ return {
161
+ ttl: post.title,
162
+ aut: this._autFromAuthor(post.author),
163
+ lut: this._lutFromLastUpdatedMillis(post.lastUpdatedMillis)
164
+ };
165
+ }
166
+
167
+ fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
168
+ const data = snapshot.data(options) as PostDbModel;
169
+ const author = ` $ {data .aut .firstName } $ {data .aut .lastName }` ;
170
+ return new Post(data.ttl, author, data.lut.toMillis());
168
171
}
169
- const [firstName, lastName] = author.split(' ');
170
- return { firstName, lastName };
171
- },
172
-
173
- _lutFromLastUpdatedMillis(
174
- lastUpdatedMillis: number | FieldValue
175
- ): Timestamp | FieldValue {
176
- if (typeof lastUpdatedMillis !== 'number') {
177
- // ` lastUpdatedMillis ` must be a FieldValue, so just return it.
178
- return lastUpdatedMillis ;
172
+
173
+ _autFromAuthor(
174
+ author: string | FieldValue
175
+ ): { firstName: string; lastName: string } | FieldValue {
176
+ if (typeof author !== 'string') {
177
+ // ` author ` is a FieldValue, so just return it.
178
+ return author;
179
+ }
180
+ const [firstName, lastName] = author.split(' ');
181
+ return {firstName, lastName} ;
179
182
}
180
- return Timestamp.fromMillis(lastUpdatedMillis);
181
- }
182
- };
183
+
184
+ _lutFromLastUpdatedMillis(
185
+ lastUpdatedMillis: number | FieldValue
186
+ ): Timestamp | FieldValue {
187
+ if (typeof lastUpdatedMillis !== 'number') {
188
+ // ` lastUpdatedMillis ` must be a FieldValue, so just return it.
189
+ return lastUpdatedMillis;
190
+ }
191
+ return Timestamp.fromMillis(lastUpdatedMillis);
192
+ }
193
+ }
183
194
184
195
async function advancedDemo(db: Firestore): Promise<void> {
185
- // Create a ` DocumentReference ` with a ` FirestoreDataConverter ` .
186
- const documentRef = doc(db, 'posts/post123').withConverter(postConverter);
187
-
188
- // The ` data ` argument specified to ` setDoc ()` is type checked by the
189
- // TypeScript compiler to be compatible with ` Post ` . Since the ` data `
190
- // argument is typed as ` WithFieldValue <Post >` rather than just ` Post ` ,
191
- // this allows properties of the ` data ` argument to also be special
192
- // Firestore values that perform server-side mutations, such as
193
- // ` arrayRemove ()` , ` deleteField ()` , and ` serverTimestamp ()` .
194
- await setDoc(documentRef, {
195
- title: 'My Life',
196
- author: 'Foo Bar',
197
- lastUpdatedMillis: serverTimestamp()
198
- });
199
-
200
- // The TypeScript compiler will fail to compile if the ` data ` argument to
201
- // ` setDoc ()` is _not_ be compatible with ` WithFieldValue <Post >` . This
202
- // type checking prevents the caller from specifying objects with incorrect
203
- // properties or property values.
204
- // @ts-expect-error "Argument of type { ttl: string; } is not assignable
205
- // to parameter of type WithFieldValue<Post>"
206
- await setDoc(documentRef, { ttl: 'The Title' });
207
-
208
- // When retrieving a document with ` getDoc ()` the ` DocumentSnapshot `
209
- // object's ` data ()` method returns a ` Post ` , rather than a generic object,
210
- // which is returned if the ` DocumentReference ` did _not_ have a
211
- // ` FirestoreDataConverter ` attached to it.
212
- const snapshot1: DocumentSnapshot<Post> = await getDoc(documentRef);
213
- const post1: Post = snapshot1.data()!;
214
- if (post1) {
215
- assertEqual(post1.title, 'My Life');
216
- assertEqual(post1.author, 'Foo Bar');
217
- }
218
-
219
- // The ` data ` argument specified to ` updateDoc ()` is type checked by the
220
- // TypeScript compiler to be compatible with ` PostDbModel ` . Note that
221
- // unlike ` setDoc ()` , whose ` data ` argument must be compatible with ` Post ` ,
222
- // the ` data ` argument to ` updateDoc ()` must be compatible with
223
- // ` PostDbModel ` . Similar to ` setDoc ()` , since the ` data ` argument is typed
224
- // as ` WithFieldValue <PostDbModel >` rather than just ` PostDbModel ` , this
225
- // allows properties of the ` data ` argument to also be those special
226
- // Firestore values, like as ` arrayRemove ()` , ` deleteField ()` , and
227
- // ` serverTimestamp ()` .
228
- await updateDoc(documentRef, {
229
- 'aut.firstName': 'NewFirstName',
230
- lut: serverTimestamp()
231
- });
232
-
233
- // The TypeScript compiler will fail to compile if the ` data ` argument to
234
- // ` updateDoc ()` is _not_ be compatible with ` WithFieldValue <PostDbModel >` .
235
- // This type checking prevents the caller from specifying objects with
236
- // incorrect properties or property values.
237
- // @ts-expect-error "Argument of type { title: string; } is not assignable
238
- // to parameter of type WithFieldValue<PostDbModel>"
239
- await updateDoc(documentRef, { title: 'New Title' });
240
-
241
- const snapshot2: DocumentSnapshot<Post> = await getDoc(documentRef);
242
- const post2: Post = snapshot2.data()!;
243
- if (post2) {
244
- assertEqual(post2.title, 'My Life');
245
- assertEqual(post2.author, 'NewFirstName Bar');
246
- }
196
+ // Create a ` DocumentReference ` with a ` FirestoreDataConverter ` .
197
+ const documentRef = doc(db, 'posts/post123').withConverter(new PostConverter());
198
+
199
+ // The ` data ` argument specified to ` setDoc ()` is type checked by the
200
+ // TypeScript compiler to be compatible with ` Post ` . Since the ` data `
201
+ // argument is typed as ` WithFieldValue <Post >` rather than just ` Post ` ,
202
+ // this allows properties of the ` data ` argument to also be special
203
+ // Firestore values that perform server-side mutations, such as
204
+ // ` arrayRemove ()` , ` deleteField ()` , and ` serverTimestamp ()` .
205
+ await setDoc(documentRef, {
206
+ title: 'My Life',
207
+ author: 'Foo Bar',
208
+ lastUpdatedMillis: serverTimestamp()
209
+ });
210
+
211
+ // The TypeScript compiler will fail to compile if the ` data ` argument to
212
+ // ` setDoc ()` is _not_ be compatible with ` WithFieldValue <Post >` . This
213
+ // type checking prevents the caller from specifying objects with incorrect
214
+ // properties or property values.
215
+ // @ts-expect-error "Argument of type { ttl: string; } is not assignable
216
+ // to parameter of type WithFieldValue<Post>"
217
+ await setDoc(documentRef, { ttl: 'The Title' });
218
+
219
+ // When retrieving a document with ` getDoc ()` the ` DocumentSnapshot `
220
+ // object's ` data ()` method returns a ` Post ` , rather than a generic object,
221
+ // which would have been returned if the ` DocumentReference ` did _not_ have a
222
+ // ` FirestoreDataConverter ` attached to it.
223
+ const snapshot1: DocumentSnapshot<Post> = await getDoc(documentRef);
224
+ const post1: Post = snapshot1.data()!;
225
+ if (post1) {
226
+ assertEqual(post1.title, 'My Life');
227
+ assertEqual(post1.author, 'Foo Bar');
228
+ }
229
+
230
+ // The ` data ` argument specified to ` updateDoc ()` is type checked by the
231
+ // TypeScript compiler to be compatible with ` PostDbModel ` . Note that
232
+ // unlike ` setDoc ()` , whose ` data ` argument must be compatible with ` Post ` ,
233
+ // the ` data ` argument to ` updateDoc ()` must be compatible with
234
+ // ` PostDbModel ` . Similar to ` setDoc ()` , since the ` data ` argument is typed
235
+ // as ` WithFieldValue <PostDbModel >` rather than just ` PostDbModel ` , this
236
+ // allows properties of the ` data ` argument to also be those special
237
+ // Firestore values, like as ` arrayRemove ()` , ` deleteField ()` , and
238
+ // ` serverTimestamp ()` .
239
+ await updateDoc(documentRef, {
240
+ 'aut.firstName': 'NewFirstName',
241
+ lut: serverTimestamp()
242
+ });
243
+
244
+ // The TypeScript compiler will fail to compile if the ` data ` argument to
245
+ // ` updateDoc ()` is _not_ be compatible with ` WithFieldValue <PostDbModel >` .
246
+ // This type checking prevents the caller from specifying objects with
247
+ // incorrect properties or property values.
248
+ // @ts-expect-error "Argument of type { title: string; } is not assignable
249
+ // to parameter of type WithFieldValue<PostDbModel>"
250
+ await updateDoc(documentRef, { title: 'New Title' });
251
+ const snapshot2: DocumentSnapshot<Post> = await getDoc(documentRef);
252
+ const post2: Post = snapshot2.data()!;
253
+ if (post2) {
254
+ assertEqual(post2.title, 'My Life');
255
+ assertEqual(post2.author, 'NewFirstName Bar');
256
+ }
247
257
}
248
258
249
259
` ` `
0 commit comments