@@ -51,40 +51,115 @@ import { SnapshotListenOptions } from './reference_impl';
51
51
* @example
52
52
* ```typescript
53
53
* class Post {
54
- * constructor(readonly title: string, readonly author: string) {}
54
+ * constructor(
55
+ * readonly title: string,
56
+ * readonly author: string,
57
+ * readonly lastUpdatedMillis: number
58
+ * ) {}
55
59
*
56
60
* toString(): string {
57
- * return this.title + ', by ' + this.author;
61
+ * return `${ this.title} by ${ this.author}` ;
58
62
* }
59
63
* }
60
64
*
61
65
* interface PostDbModel {
62
- * title: string;
63
- * author: string;
66
+ * ttl: string;
67
+ * aut: { firstName: string; lastName: string };
68
+ * lut: Timestamp;
64
69
* }
65
70
*
66
71
* const postConverter = {
67
- * toFirestore(post: WithFieldValue<Post>): PostDbModel {
68
- * return {title: post.title, author: post.author};
72
+ * toFirestore(post: WithFieldValue<Post>) {
73
+ * return {
74
+ * ttl: post.title,
75
+ * aut: this._autFromAuthor(post.author),
76
+ * lut: this._lutFromLastUpdatedMillis(post.lastUpdatedMillis)
77
+ * };
69
78
* },
70
- * fromFirestore(
71
- * snapshot: QueryDocumentSnapshot,
72
- * options: SnapshotOptions
73
- * ): Post {
79
+ *
80
+ * fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
74
81
* const data = snapshot.data(options) as PostDbModel;
75
- * return new Post(data.title, data.author);
82
+ * const author = `${data.aut.firstName} ${data.aut.lastName}`;
83
+ * return new Post(data.ttl, author, data.lut.toMillis());
84
+ * },
85
+ *
86
+ * _autFromAuthor(
87
+ * author: string | FieldValue
88
+ * ): { firstName: string; lastName: string } | FieldValue {
89
+ * if (typeof author !== 'string') {
90
+ * // `author` is a FieldValue, so just return it.
91
+ * return author;
92
+ * }
93
+ * const [firstName, lastName] = author.split(' ');
94
+ * return { firstName, lastName };
95
+ * },
96
+ *
97
+ * _lutFromLastUpdatedMillis(
98
+ * lastUpdatedMillis: number | FieldValue
99
+ * ): Timestamp | FieldValue {
100
+ * if (typeof lastUpdatedMillis !== 'number') {
101
+ * // `lastUpdatedMillis` must be a FieldValue, so just return it.
102
+ * return lastUpdatedMillis;
103
+ * }
104
+ * return Timestamp.fromMillis(lastUpdatedMillis);
76
105
* }
77
106
* };
78
107
*
79
- * const postSnap = await firebase.firestore()
80
- * .collection('posts')
81
- * .withConverter(postConverter)
82
- * .doc().get();
83
- * const post = postSnap.data();
84
- * if (post !== undefined) {
85
- * post.title; // string
86
- * post.toString(); // Should be defined
87
- * post.someNonExistentProperty; // TS error
108
+ * async function demo(db: Firestore): Promise<void> {
109
+ * // Create a `DocumentReference` with a `FirestoreDataConverter`.
110
+ * const documentRef = doc(db, 'posts/post123').withConverter(postConverter);
111
+ *
112
+ * // The `data` argument specified to `setDoc()` is type checked by the
113
+ * // TypeScript compiler to be compatible with `Post`. Since the `data`
114
+ * // argument is typed as `WithFieldValue<Post>` rather than just `Post`,
115
+ * // this allows properties of the `data` argument to also be special
116
+ * // Firestore values that perform server-side mutations, such as
117
+ * // `arrayRemove()`, `deleteField()`, and `serverTimestamp()`.
118
+ * await setDoc(documentRef, {
119
+ * title: 'My Life',
120
+ * author: 'Foo Bar',
121
+ * lastUpdatedMillis: serverTimestamp()
122
+ * });
123
+ *
124
+ * // The TypeScript compiler will fail to compile if the `data` argument to
125
+ * // `setDoc()` is _not_ be compatible with `WithFieldValue<Post>`. This
126
+ * // type checking prevents the caller from specifying objects with incorrect
127
+ * // properties or property values.
128
+ * // @ts -expect-error "Argument of type { ttl: String; } is not assignable to
129
+ * // parameter of type WithFieldValue<Post>"
130
+ * await setDoc(documentRef, { ttl: 'The Title' });
131
+ *
132
+ * // When retrieving a document with `getDoc()` the `DocumentSnapshot`
133
+ * // object's `data()` method returns a `Post`, rather than a generic object,
134
+ * // which is returned if the `DocumentReference` did _not_ have a
135
+ * // `FirestoreDataConverter` attached to it.
136
+ * const postSnap: DocumentSnapshot<Post> = await getDoc(documentRef);
137
+ * const post: Post | undefined = postSnap.data();
138
+ * if (post) {
139
+ * console.log(`Post ${documentRef.path} has title=${post.title}`);
140
+ * }
141
+ *
142
+ * // The `data` argument specified to `updateDoc()` is type checked by the
143
+ * // TypeScript compiler to be compatible with `PostDbModel`. Note that
144
+ * // unlike `setDoc()`, whose `data` argument must be compatible with `Post`,
145
+ * // the `data` argument to `updateDoc()` must be compatible with
146
+ * // `PostDbModel`. Similar to `setDoc()`, since the `data` argument is typed
147
+ * // as `WithFieldValue<PostDbModel>` rather than just `PostDbModel`, this
148
+ * // allows properties of the `data` argument to also be those special
149
+ * // Firestore values, like as `arrayRemove()`, `deleteField()`, and
150
+ * // `serverTimestamp()`.
151
+ * await updateDoc(documentRef, {
152
+ * 'aut.firstName': 'NewFirstName',
153
+ * lut: serverTimestamp()
154
+ * });
155
+ *
156
+ * // The TypeScript compiler will fail to compile if the `data` argument to
157
+ * // `updateDoc()` is _not_ be compatible with `WithFieldValue<PostDbModel>`.
158
+ * // This type checking prevents the caller from specifying objects with
159
+ * // incorrect properties or property values.
160
+ * // @ts -expect-error "Argument of type { title: String; } is not assignable
161
+ * // to parameter of type WithFieldValue<PostDbModel>"
162
+ * await updateDoc(documentRef, { title: 'New Title' });
88
163
* }
89
164
* ```
90
165
*/
0 commit comments