Skip to content

Commit 4371dbc

Browse files
author
Brian Chen
committed
WIP: working NestedParial and WithFieldValue
1 parent 8893c82 commit 4371dbc

File tree

9 files changed

+71
-25
lines changed

9 files changed

+71
-25
lines changed

packages/firestore/src/api/database.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ import {
127127
NextFn,
128128
PartialObserver
129129
} from './observer';
130+
import { NestedPartialWithFieldValue, WithFieldValue } from '../lite/reference';
130131

131132
/**
132133
* A persistence provider for either memory-only or IndexedDB persistence.
@@ -597,19 +598,22 @@ class FirestoreDataConverter<U>
597598
);
598599
}
599600

600-
toFirestore(modelObject: U): PublicDocumentData;
601+
toFirestore(modelObject: WithFieldValue<U>): PublicDocumentData;
601602
toFirestore(
602-
modelObject: Partial<U>,
603+
modelObject: NestedPartialWithFieldValue<U>,
603604
options: PublicSetOptions
604605
): PublicDocumentData;
605606
toFirestore(
606-
modelObject: U | Partial<U>,
607+
modelObject: WithFieldValue<U> | NestedPartialWithFieldValue<U>,
607608
options?: PublicSetOptions
608609
): PublicDocumentData {
609610
if (!options) {
610611
return this._delegate.toFirestore(modelObject as U);
611612
} else {
612-
return this._delegate.toFirestore(modelObject, options);
613+
return this._delegate.toFirestore(
614+
modelObject as Partial<U>,
615+
options
616+
);
613617
}
614618
}
615619

packages/firestore/src/exp/snapshot.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@
1818
import { newQueryComparator } from '../core/query';
1919
import { ChangeType, ViewSnapshot } from '../core/view_snapshot';
2020
import { FieldPath } from '../lite/field_path';
21-
import { DocumentData, Query, queryEqual, SetOptions } from '../lite/reference';
21+
import {
22+
DocumentData,
23+
NestedPartialWithFieldValue,
24+
Query,
25+
queryEqual,
26+
SetOptions,
27+
WithFieldValue
28+
} from '../lite/reference';
2229
import {
2330
DocumentSnapshot as LiteDocumentSnapshot,
2431
fieldPathFromArgument,
@@ -84,15 +91,15 @@ export interface FirestoreDataConverter<T>
8491
* Firestore database). To use `set()` with `merge` and `mergeFields`,
8592
* `toFirestore()` must be defined with `Partial<T>`.
8693
*/
87-
toFirestore(modelObject: T): DocumentData;
94+
toFirestore(modelObject: WithFieldValue<T>): DocumentData;
8895

8996
/**
9097
* Called by the Firestore SDK to convert a custom model object of type `T`
9198
* into a plain JavaScript object (suitable for writing directly to the
9299
* Firestore database). Used with {@link (setDoc:1)}, {@link (WriteBatch.set:1)}
93100
* and {@link (Transaction.set:1)} with `merge:true` or `mergeFields`.
94101
*/
95-
toFirestore(modelObject: Partial<T>, options: SetOptions): DocumentData;
102+
toFirestore(modelObject: NestedPartialWithFieldValue<T>, options: SetOptions): DocumentData;
96103

97104
/**
98105
* Called by the Firestore SDK to convert Firestore data into an object of

packages/firestore/src/lite/reference.ts

+16
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { AutoId } from '../util/misc';
3636

3737
import { Firestore } from './database';
3838
import { FieldPath } from './field_path';
39+
import { FieldValue } from './field_value';
3940
import { FirestoreDataConverter } from './snapshot';
4041

4142
/**
@@ -48,6 +49,21 @@ export interface DocumentData {
4849
[field: string]: any;
4950
}
5051

52+
type Primitive = string | number | boolean | bigint | undefined | null;
53+
// eslint-disable-next-line @typescript-eslint/ban-types
54+
type Builtin = Primitive | Function;
55+
56+
/** Like Partial but recursive */
57+
export type NestedPartialWithFieldValue<T> = T extends Builtin
58+
? T
59+
: T extends Map<infer K, infer V>
60+
? Map<NestedPartialWithFieldValue<K>, NestedPartialWithFieldValue<V>>
61+
: T extends {}
62+
? { [K in keyof T]?: NestedPartialWithFieldValue<T[K]> | FieldValue }
63+
: Partial<T>;
64+
65+
export type WithFieldValue<T> = { [P in keyof T]: T[P] | FieldValue };
66+
5167
/**
5268
* Update data (for use with {@link @firebase/firestore/lite#(updateDoc:1)}) consists of field paths (e.g.
5369
* 'foo' or 'foo.baz') mapped to values. Fields that contain dots reference

packages/firestore/src/lite/reference_impl.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ import {
4141
CollectionReference,
4242
doc,
4343
DocumentReference,
44+
NestedPartialWithFieldValue,
4445
Query,
4546
SetOptions,
46-
UpdateData
47+
UpdateData,
48+
WithFieldValue
4749
} from './reference';
4850
import {
4951
DocumentSnapshot,
@@ -197,7 +199,7 @@ export function getDocs<T>(query: Query<T>): Promise<QuerySnapshot<T>> {
197199
*/
198200
export function setDoc<T>(
199201
reference: DocumentReference<T>,
200-
data: T
202+
data: WithFieldValue<T>
201203
): Promise<void>;
202204
/**
203205
* Writes to the document referred to by the specified `DocumentReference`. If
@@ -217,12 +219,12 @@ export function setDoc<T>(
217219
*/
218220
export function setDoc<T>(
219221
reference: DocumentReference<T>,
220-
data: Partial<T>,
222+
data: NestedPartialWithFieldValue<T>,
221223
options: SetOptions
222224
): Promise<void>;
223225
export function setDoc<T>(
224226
reference: DocumentReference<T>,
225-
data: T,
227+
data: WithFieldValue<T> | NestedPartialWithFieldValue<T>,
226228
options?: SetOptions
227229
): Promise<void> {
228230
reference = cast<DocumentReference<T>>(reference, DocumentReference);

packages/firestore/src/lite/snapshot.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ import { FieldPath } from './field_path';
2727
import {
2828
DocumentData,
2929
DocumentReference,
30+
NestedPartialWithFieldValue,
3031
Query,
3132
queryEqual,
32-
SetOptions
33+
SetOptions,
34+
WithFieldValue
3335
} from './reference';
3436
import {
3537
fieldPathFromDotSeparatedString,
@@ -83,15 +85,15 @@ export interface FirestoreDataConverter<T> {
8385
* Firestore database). Used with {@link @firebase/firestore/lite#(setDoc:1)}, {@link @firebase/firestore/lite#(WriteBatch.set:1)}
8486
* and {@link @firebase/firestore/lite#(Transaction.set:1)}.
8587
*/
86-
toFirestore(modelObject: T): DocumentData;
88+
toFirestore(modelObject: WithFieldValue<T>): DocumentData;
8789

8890
/**
8991
* Called by the Firestore SDK to convert a custom model object of type `T`
9092
* into a plain Javascript object (suitable for writing directly to the
9193
* Firestore database). Used with {@link @firebase/firestore/lite#(setDoc:1)}, {@link @firebase/firestore/lite#(WriteBatch.set:1)}
9294
* and {@link @firebase/firestore/lite#(Transaction.set:1)} with `merge:true` or `mergeFields`.
9395
*/
94-
toFirestore(modelObject: Partial<T>, options: SetOptions): DocumentData;
96+
toFirestore(modelObject: NestedPartialWithFieldValue<T>, options: SetOptions): DocumentData;
9597

9698
/**
9799
* Called by the Firestore SDK to convert Firestore data into an object of

packages/firestore/src/lite/user_data_reader.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ import { Firestore } from './database';
6363
import { FieldPath } from './field_path';
6464
import { FieldValue } from './field_value';
6565
import { GeoPoint } from './geo_point';
66-
import { DocumentReference } from './reference';
66+
import { DocumentReference, NestedPartialWithFieldValue, WithFieldValue } from './reference';
6767
import { Timestamp } from './timestamp';
6868

6969
const RESERVED_FIELD_REGEX = /^__.*__$/;
@@ -73,8 +73,8 @@ const RESERVED_FIELD_REGEX = /^__.*__$/;
7373
* lite, firestore-exp and classic SDK.
7474
*/
7575
export interface UntypedFirestoreDataConverter<T> {
76-
toFirestore(modelObject: T): DocumentData;
77-
toFirestore(modelObject: Partial<T>, options: SetOptions): DocumentData;
76+
toFirestore(modelObject: WithFieldValue<T>): DocumentData;
77+
toFirestore(modelObject: NestedPartialWithFieldValue<T>, options: SetOptions): DocumentData;
7878
fromFirestore(snapshot: unknown, options?: unknown): T;
7979
}
8080

packages/firestore/test/integration/api/database.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
withTestDocAndInitialData
3333
} from '../util/helpers';
3434
import { DEFAULT_SETTINGS, DEFAULT_PROJECT_ID } from '../util/settings';
35+
import { NestedPartialWithFieldValue } from '../../../src/lite/reference';
3536

3637
use(chaiAsPromised);
3738

@@ -1351,7 +1352,7 @@ apiDescribe('Database', (persistence: boolean) => {
13511352

13521353
const postConverterMerge = {
13531354
toFirestore(
1354-
post: Partial<Post>,
1355+
post: NestedPartialWithFieldValue<Post>,
13551356
options?: firestore.SetOptions
13561357
): firestore.DocumentData {
13571358
if (options && (options.merge || options.mergeFields)) {

packages/firestore/test/lite/helpers.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import {
2626
DocumentData,
2727
CollectionReference,
2828
DocumentReference,
29-
SetOptions
29+
SetOptions,
30+
NestedPartialWithFieldValue
3031
} from '../../src/lite/reference';
3132
import { setDoc } from '../../src/lite/reference_impl';
3233
import { FirestoreSettings } from '../../src/lite/settings';
@@ -102,7 +103,11 @@ export function withTestCollection(
102103

103104
// Used for testing the FirestoreDataConverter.
104105
export class Post {
105-
constructor(readonly title: string, readonly author: string) {}
106+
constructor(
107+
readonly title: string,
108+
readonly author: string,
109+
readonly id = 1
110+
) {}
106111
byline(): string {
107112
return this.title + ', by ' + this.author;
108113
}
@@ -119,7 +124,7 @@ export const postConverter = {
119124
};
120125

121126
export const postConverterMerge = {
122-
toFirestore(post: Partial<Post>, options?: SetOptions): DocumentData {
127+
toFirestore(post: NestedPartialWithFieldValue<Post>, options?: SetOptions): DocumentData {
123128
if (
124129
options &&
125130
((options as { merge: true }).merge ||

packages/firestore/test/lite/integration.test.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ import {
5757
collectionGroup,
5858
SetOptions,
5959
UpdateData,
60-
DocumentData
60+
DocumentData,
61+
WithFieldValue,
62+
NestedPartialWithFieldValue
6163
} from '../../src/lite/reference';
6264
import {
6365
addDoc,
@@ -337,10 +339,13 @@ describe('getDoc()', () => {
337339
* DocumentReference-based mutation API.
338340
*/
339341
interface MutationTester {
340-
set<T>(documentRef: DocumentReference<T>, data: T): Promise<void>;
341342
set<T>(
342343
documentRef: DocumentReference<T>,
343-
data: Partial<T>,
344+
data: WithFieldValue<T>
345+
): Promise<void>;
346+
set<T>(
347+
documentRef: DocumentReference<T>,
348+
data: NestedPartialWithFieldValue<T>,
344349
options: SetOptions
345350
): Promise<void>;
346351
update(
@@ -580,7 +585,11 @@ function genericMutationTests(
580585
const coll = collection(db, 'posts');
581586
const ref = doc(coll, 'post').withConverter(postConverterMerge);
582587
await setDoc(ref, new Post('walnut', 'author'));
583-
await setDoc(ref, { title: 'olive' }, { merge: true });
588+
await setDoc(
589+
ref,
590+
{ title: 'olive', id: increment(2) },
591+
{ merge: true }
592+
);
584593
const postDoc = await getDoc(ref);
585594
expect(postDoc.get('title')).to.equal('olive');
586595
expect(postDoc.get('author')).to.equal('author');

0 commit comments

Comments
 (0)