diff --git a/.changeset/olive-geckos-argue.md b/.changeset/olive-geckos-argue.md new file mode 100644 index 00000000000..b5a4990e20d --- /dev/null +++ b/.changeset/olive-geckos-argue.md @@ -0,0 +1,5 @@ +--- +"@firebase/firestore": patch +--- + +UpdateData allows indexed types or Record for T. diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 440fa488c1c..d884120efe6 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -75,6 +75,11 @@ export class Bytes { toUint8Array(): Uint8Array; } +// @public +export type ChildTypes = T extends Record ? { + [K in keyof T & string]: ChildTypes; +}[keyof T & string] | T : T; + // @public export type ChildUpdateFields = V extends Record ? AddPrefixToKeys> : never; @@ -259,7 +264,7 @@ export { LogLevel } // @public export type NestedUpdateFields> = UnionToIntersection<{ - [K in keyof T & string]: ChildUpdateFields; + [K in keyof T & string]: string extends K ? never : ChildUpdateFields; }[keyof T & string]>; // @public @@ -451,7 +456,7 @@ export type UnionToIntersection = (U extends unknown ? (k: U) => void : never // @public export type UpdateData = T extends Primitive ? T : T extends {} ? { - [K in keyof T]?: UpdateData | FieldValue; + [K in keyof T]?: string extends K ? PartialWithFieldValue> : UpdateData | FieldValue; } & NestedUpdateFields : Partial; // @public diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index ee4fcc842ff..7843afb82aa 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -78,6 +78,11 @@ export class Bytes { // @public export const CACHE_SIZE_UNLIMITED = -1; +// @public +export type ChildTypes = T extends Record ? { + [K in keyof T & string]: ChildTypes; +}[keyof T & string] | T : T; + // @public export type ChildUpdateFields = V extends Record ? AddPrefixToKeys> : never; @@ -413,7 +418,7 @@ export function namedQuery(firestore: Firestore, name: string): Promise> = UnionToIntersection<{ - [K in keyof T & string]: ChildUpdateFields; + [K in keyof T & string]: string extends K ? never : ChildUpdateFields; }[keyof T & string]>; // @public @@ -732,7 +737,7 @@ export interface Unsubscribe { // @public export type UpdateData = T extends Primitive ? T : T extends {} ? { - [K in keyof T]?: UpdateData | FieldValue; + [K in keyof T]?: string extends K ? PartialWithFieldValue> : UpdateData | FieldValue; } & NestedUpdateFields : Partial; // @public diff --git a/docs-devsite/firestore_.md b/docs-devsite/firestore_.md index ba7f6270621..ef3035d25d0 100644 --- a/docs-devsite/firestore_.md +++ b/docs-devsite/firestore_.md @@ -200,6 +200,7 @@ https://github.com/firebase/firebase-js-sdk | [AggregateFieldType](./firestore_.md#aggregatefieldtype) | The union of all AggregateField types that are supported by Firestore. | | [AggregateSpecData](./firestore_.md#aggregatespecdata) | A type whose keys are taken from an AggregateSpec, and whose values are the result of the aggregation performed by the corresponding AggregateField from the input AggregateSpec. | | [AggregateType](./firestore_.md#aggregatetype) | Union type representing the aggregate type to be performed. | +| [ChildTypes](./firestore_.md#childtypes) | For the given type, return a union type of T and the types of all child properties of T. | | [ChildUpdateFields](./firestore_.md#childupdatefields) | Helper for calculating the nested fields for a given type T1. This is needed to distribute union types such as undefined | {...} (happens for optional props) or {a: A} | {b: B}.In this use case, V is used to distribute the union types of T[K] on Record, since T[K] is evaluated as an expression and not distributed.See https://www.typescriptlang.org/docs/handbook/advanced-types.html\#distributive-conditional-types | | [DocumentChangeType](./firestore_.md#documentchangetype) | The type of a DocumentChange may be 'added', 'removed', or 'modified'. | | [FirestoreErrorCode](./firestore_.md#firestoreerrorcode) | The set of Firestore status codes. The codes are the same at the ones exposed by gRPC here: https://github.com/grpc/grpc/blob/master/doc/statuscodes.mdPossible values: - 'cancelled': The operation was cancelled (typically by the caller). - 'unknown': Unknown error or an error from a different error domain. - 'invalid-argument': Client specified an invalid argument. Note that this differs from 'failed-precondition'. 'invalid-argument' indicates arguments that are problematic regardless of the state of the system (e.g. an invalid field name). - 'deadline-exceeded': Deadline expired before operation could complete. For operations that change the state of the system, this error may be returned even if the operation has completed successfully. For example, a successful response from a server could have been delayed long enough for the deadline to expire. - 'not-found': Some requested document was not found. - 'already-exists': Some document that we attempted to create already exists. - 'permission-denied': The caller does not have permission to execute the specified operation. - 'resource-exhausted': Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space. - 'failed-precondition': Operation was rejected because the system is not in a state required for the operation's execution. - 'aborted': The operation was aborted, typically due to a concurrency issue like transaction aborts, etc. - 'out-of-range': Operation was attempted past the valid range. - 'unimplemented': Operation is not implemented or not supported/enabled. - 'internal': Internal errors. Means some invariants expected by underlying system has been broken. If you see one of these errors, something is very broken. - 'unavailable': The service is currently unavailable. This is most likely a transient condition and may be corrected by retrying with a backoff. - 'data-loss': Unrecoverable data loss or corruption. - 'unauthenticated': The request does not have valid authentication credentials for the operation. | @@ -2505,6 +2506,18 @@ Union type representing the aggregate type to be performed. export declare type AggregateType = 'count' | 'avg' | 'sum'; ``` +## ChildTypes + +For the given type, return a union type of T and the types of all child properties of T. + +Signature: + +```typescript +export declare type ChildTypes = T extends Record ? { + [K in keyof T & string]: ChildTypes; +}[keyof T & string] | T : T; +``` + ## ChildUpdateFields Helper for calculating the nested fields for a given type T1. This is needed to distribute union types such as `undefined | {...}` (happens for optional props) or `{a: A} | {b: B}`. @@ -2569,7 +2582,7 @@ For each field (e.g. 'bar'), find all nested keys (e.g. {'bar.baz': T1, ```typescript export declare type NestedUpdateFields> = UnionToIntersection<{ - [K in keyof T & string]: ChildUpdateFields; + [K in keyof T & string]: string extends K ? never : ChildUpdateFields; }[keyof T & string]>; ``` @@ -2691,7 +2704,7 @@ Update data (for use with [updateDoc()](./firestore_.md#updatedoc_51a65e3)In this use case, V is used to distribute the union types of T[K] on Record, since T[K] is evaluated as an expression and not distributed.See https://www.typescriptlang.org/docs/handbook/advanced-types.html\#distributive-conditional-types | | [FirestoreErrorCode](./firestore_lite.md#firestoreerrorcode) | The set of Firestore status codes. The codes are the same at the ones exposed by gRPC here: https://github.com/grpc/grpc/blob/master/doc/statuscodes.mdPossible values: - 'cancelled': The operation was cancelled (typically by the caller). - 'unknown': Unknown error or an error from a different error domain. - 'invalid-argument': Client specified an invalid argument. Note that this differs from 'failed-precondition'. 'invalid-argument' indicates arguments that are problematic regardless of the state of the system (e.g. an invalid field name). - 'deadline-exceeded': Deadline expired before operation could complete. For operations that change the state of the system, this error may be returned even if the operation has completed successfully. For example, a successful response from a server could have been delayed long enough for the deadline to expire. - 'not-found': Some requested document was not found. - 'already-exists': Some document that we attempted to create already exists. - 'permission-denied': The caller does not have permission to execute the specified operation. - 'resource-exhausted': Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space. - 'failed-precondition': Operation was rejected because the system is not in a state required for the operation's execution. - 'aborted': The operation was aborted, typically due to a concurrency issue like transaction aborts, etc. - 'out-of-range': Operation was attempted past the valid range. - 'unimplemented': Operation is not implemented or not supported/enabled. - 'internal': Internal errors. Means some invariants expected by underlying system has been broken. If you see one of these errors, something is very broken. - 'unavailable': The service is currently unavailable. This is most likely a transient condition and may be corrected by retrying with a backoff. - 'data-loss': Unrecoverable data loss or corruption. - 'unauthenticated': The request does not have valid authentication credentials for the operation. | | [NestedUpdateFields](./firestore_lite.md#nestedupdatefields) | For each field (e.g. 'bar'), find all nested keys (e.g. {'bar.baz': T1, 'bar.qux': T2}). Intersect them together to make a single map containing all possible keys that are all marked as optional | @@ -1612,6 +1613,18 @@ Union type representing the aggregate type to be performed. export declare type AggregateType = 'count' | 'avg' | 'sum'; ``` +## ChildTypes + +For the given type, return a union type of T and the types of all child properties of T. + +Signature: + +```typescript +export declare type ChildTypes = T extends Record ? { + [K in keyof T & string]: ChildTypes; +}[keyof T & string] | T : T; +``` + ## ChildUpdateFields Helper for calculating the nested fields for a given type T1. This is needed to distribute union types such as `undefined | {...}` (happens for optional props) or `{a: A} | {b: B}`. @@ -1646,7 +1659,7 @@ For each field (e.g. 'bar'), find all nested keys (e.g. {'bar.baz': T1, ```typescript export declare type NestedUpdateFields> = UnionToIntersection<{ - [K in keyof T & string]: ChildUpdateFields; + [K in keyof T & string]: string extends K ? never : ChildUpdateFields; }[keyof T & string]>; ``` @@ -1746,7 +1759,7 @@ Update data (for use with [updateDoc()](./firestore_.md#updatedoc_51a65e3)