Skip to content

Commit

Permalink
Firestore: QoL improvements for converters (#5268)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Chen committed Aug 18, 2021
1 parent f35b4e3 commit 5bc6afb
Show file tree
Hide file tree
Showing 20 changed files with 1,367 additions and 606 deletions.
10 changes: 10 additions & 0 deletions .changeset/dirty-pandas-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'firebase': major
'@firebase/firestore': major
---

This change contains multiple quality-of-life improvements when using the `FirestoreDataConverter` in `@firebase/firestore/lite` and `@firebase/firestore`:
- Support for passing in `FieldValue` property values when using a converter (via `WithFieldValue<T>` and `PartialWithFieldValue<T>`).
- Support for omitting properties in nested fields when performing a set operation with `{merge: true}` with a converter (via `PartialWithFieldValue<T>`).
- Support for typed update operations when using a converter (via the newly typed `UpdateData`). Improperly typed fields in
update operations on typed document references will no longer compile.
56 changes: 41 additions & 15 deletions common/api-review/firestore-lite.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import { FirebaseApp } from '@firebase/app-exp';
import { LogLevelString as LogLevel } from '@firebase/logger';

// @public
export function addDoc<T>(reference: CollectionReference<T>, data: T): Promise<DocumentReference<T>>;
export function addDoc<T>(reference: CollectionReference<T>, data: WithFieldValue<T>): Promise<DocumentReference<T>>;

// @public
export type AddPrefixToKeys<Prefix extends string, T extends Record<string, unknown>> = {
[K in keyof T & string as `${Prefix}.${K}`]+?: T[K];
};

// @public
export function arrayRemove(...elements: unknown[]): FieldValue;
Expand Down Expand Up @@ -132,8 +137,8 @@ export class Firestore {
// @public
export interface FirestoreDataConverter<T> {
fromFirestore(snapshot: QueryDocumentSnapshot<DocumentData>): T;
toFirestore(modelObject: T): DocumentData;
toFirestore(modelObject: Partial<T>, options: SetOptions): DocumentData;
toFirestore(modelObject: WithFieldValue<T>): DocumentData;
toFirestore(modelObject: PartialWithFieldValue<T>, options: SetOptions): DocumentData;
}

// @public
Expand Down Expand Up @@ -182,12 +187,25 @@ export function limitToLast(limit: number): QueryConstraint;

export { LogLevel }

// @public
export type NestedUpdateFields<T extends Record<string, unknown>> = UnionToIntersection<{
[K in keyof T & string]: T[K] extends Record<string, unknown> ? AddPrefixToKeys<K, UpdateData<T[K]>> : never;
}[keyof T & string]>;

// @public
export function orderBy(fieldPath: string | FieldPath, directionStr?: OrderByDirection): QueryConstraint;

// @public
export type OrderByDirection = 'desc' | 'asc';

// @public
export type PartialWithFieldValue<T> = T extends Primitive ? T : T extends {} ? {
[K in keyof T]?: PartialWithFieldValue<T[K]> | FieldValue;
} : Partial<T>;

// @public
export type Primitive = string | number | boolean | undefined | null;

// @public
export class Query<T = DocumentData> {
protected constructor();
Expand Down Expand Up @@ -237,10 +255,10 @@ export function runTransaction<T>(firestore: Firestore, updateFunction: (transac
export function serverTimestamp(): FieldValue;

// @public
export function setDoc<T>(reference: DocumentReference<T>, data: T): Promise<void>;
export function setDoc<T>(reference: DocumentReference<T>, data: WithFieldValue<T>): Promise<void>;

// @public
export function setDoc<T>(reference: DocumentReference<T>, data: Partial<T>, options: SetOptions): Promise<void>;
export function setDoc<T>(reference: DocumentReference<T>, data: PartialWithFieldValue<T>, options: SetOptions): Promise<void>;

// @public
export function setLogLevel(logLevel: LogLevel): void;
Expand Down Expand Up @@ -302,19 +320,22 @@ export class Timestamp {
export class Transaction {
delete(documentRef: DocumentReference<unknown>): this;
get<T>(documentRef: DocumentReference<T>): Promise<DocumentSnapshot<T>>;
set<T>(documentRef: DocumentReference<T>, data: T): this;
set<T>(documentRef: DocumentReference<T>, data: Partial<T>, options: SetOptions): this;
update(documentRef: DocumentReference<unknown>, data: UpdateData): this;
set<T>(documentRef: DocumentReference<T>, data: WithFieldValue<T>): this;
set<T>(documentRef: DocumentReference<T>, data: PartialWithFieldValue<T>, options: SetOptions): this;
update<T>(documentRef: DocumentReference<T>, data: UpdateData<T>): this;
update(documentRef: DocumentReference<unknown>, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): this;
}

// @public
export interface UpdateData {
[fieldPath: string]: any;
}
export type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

// @public
export function updateDoc(reference: DocumentReference<unknown>, data: UpdateData): Promise<void>;
export type UpdateData<T> = T extends Primitive ? T : T extends Map<infer K, infer V> ? Map<UpdateData<K>, UpdateData<V>> : T extends {} ? {
[K in keyof T]?: UpdateData<T[K]> | FieldValue;
} & NestedUpdateFields<T> : Partial<T>;

// @public
export function updateDoc<T>(reference: DocumentReference<T>, data: UpdateData<T>): Promise<void>;

// @public
export function updateDoc(reference: DocumentReference<unknown>, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): Promise<void>;
Expand All @@ -325,13 +346,18 @@ export function where(fieldPath: string | FieldPath, opStr: WhereFilterOp, value
// @public
export type WhereFilterOp = '<' | '<=' | '==' | '!=' | '>=' | '>' | 'array-contains' | 'in' | 'array-contains-any' | 'not-in';

// @public
export type WithFieldValue<T> = T extends Primitive ? T : T extends {} ? {
[K in keyof T]: WithFieldValue<T[K]> | FieldValue;
} : Partial<T>;

// @public
export class WriteBatch {
commit(): Promise<void>;
delete(documentRef: DocumentReference<unknown>): WriteBatch;
set<T>(documentRef: DocumentReference<T>, data: T): WriteBatch;
set<T>(documentRef: DocumentReference<T>, data: Partial<T>, options: SetOptions): WriteBatch;
update(documentRef: DocumentReference<unknown>, data: UpdateData): WriteBatch;
set<T>(documentRef: DocumentReference<T>, data: WithFieldValue<T>): WriteBatch;
set<T>(documentRef: DocumentReference<T>, data: PartialWithFieldValue<T>, options: SetOptions): WriteBatch;
update<T>(documentRef: DocumentReference<T>, data: UpdateData<T>): WriteBatch;
update(documentRef: DocumentReference<unknown>, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): WriteBatch;
}

Expand Down

0 comments on commit 5bc6afb

Please sign in to comment.