Skip to content

Commit 9afea69

Browse files
committed
snapshot.ts: re-write the sample code in the documentation
1 parent cca4735 commit 9afea69

File tree

2 files changed

+190
-37
lines changed

2 files changed

+190
-37
lines changed

packages/firestore/src/api/snapshot.ts

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,40 +51,115 @@ import { SnapshotListenOptions } from './reference_impl';
5151
* @example
5252
* ```typescript
5353
* 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+
* ) {}
5559
*
5660
* toString(): string {
57-
* return this.title + ', by ' + this.author;
61+
* return `${this.title} by ${this.author}`;
5862
* }
5963
* }
6064
*
6165
* interface PostDbModel {
62-
* title: string;
63-
* author: string;
66+
* ttl: string;
67+
* aut: { firstName: string; lastName: string };
68+
* lut: Timestamp;
6469
* }
6570
*
6671
* 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+
* };
6978
* },
70-
* fromFirestore(
71-
* snapshot: QueryDocumentSnapshot,
72-
* options: SnapshotOptions
73-
* ): Post {
79+
*
80+
* fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
7481
* 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);
76105
* }
77106
* };
78107
*
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' });
88163
* }
89164
* ```
90165
*/

packages/firestore/src/lite-api/snapshot.ts

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

0 commit comments

Comments
 (0)