UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

116 lines (115 loc) 7.12 kB
import type { UnionToIntersection } from "@google-cloud/firestore"; import type { ImmutableArray } from "./array.js"; import type { EntryObject } from "./entry.js"; import type { AnyCaller } from "./function.js"; import type { DeepPartial } from "./object.js"; import type { Segments } from "./string.js"; import type { Resolve } from "./types.js"; /** Data object. */ export type Data = { readonly [K in string]: unknown; }; /** Partial data object (values can be explicitly `undefined`). */ export type PartialData<T extends Data> = { readonly [K in keyof T]?: T[K] | undefined; }; /** Helper type to get the key for for a data object prop. */ export type DataKey<T extends Data> = keyof T & string; /** Helper type to get the value for a data object prop. */ export type DataValue<T extends Data> = T[DataKey<T>]; /** * Helper type to get a prop for a data object. * i.e. `DataProp<{ a: number }>` produces `readonly ["a", number"]` */ export type DataProp<T extends Data> = { readonly [K in DataKey<T>]: readonly [K, T[K]]; }[DataKey<T>]; /** * Helper type to get a flattened data object with every branch node of the data, flattened into `a.c.b` format. * i.e. `BranchData<{ a: { a2: number } }>` produces `{ "a": object, "a.a2": number }` */ export type BranchData<T extends Data> = EntryObject<BranchDataProp<T>>; /** Helper type to get the path for a flattened data object with deep paths flattened into `a.c.b` format. */ export type BranchDataPath<T extends Data> = BranchDataProp<T>[0]; /** Helper type to get the value for a flattened data object with deep paths flattened into `a.c.b` format. */ export type BranchDataValue<T extends Data> = BranchDataProp<T>[1]; /** Helper type to get the prop for a flattened data object with deep paths flattened into `a.c.b` format. */ export type BranchDataProp<T extends Data> = { readonly [K in DataKey<T>]: (T[K] extends Data ? readonly [null, T[K]] | BranchDataProp<T[K]> : readonly [null, T[K]]) extends infer E ? E extends readonly [infer KK, infer VV] ? readonly [KK extends string ? `${K}.${KK}` : K, VV] : never : never; }[DataKey<T>]; /** * Typed path tuple for every branch node of a data object (including intermediate objects). * i.e. `BranchPath<{ a: number, b: { c: string } }>` produces `readonly ["a"] | readonly ["b"] | readonly ["b", "c"]` */ export type BranchDataSegments<T extends Data> = { readonly [K in DataKey<T>]: T[K] extends Data ? readonly [K] | readonly [K, ...BranchDataSegments<T[K]>] : readonly [K]; }[DataKey<T>]; /** * Helper type to get a flattened data object with only leaf nodes of the data, flattened into `a.c.b` format. * i.e. `LeafData<{ a: { a2: number } }>` produces `{ "a.a2": number }` */ export type LeafData<T extends Data> = EntryObject<LeafDataProp<T>>; /** Helper type to get the leaf paths for a flattened data object with deep paths flattened into `a.c.b` format. */ export type LeafDataPath<T extends Data> = LeafDataProp<T>[0]; /** Helper type to get the leaf values for a flattened data object with deep paths flattened into `a.c.b` format. */ export type LeafDataValue<T extends Data> = LeafDataProp<T>[1]; /** Helper type to get the leaf props for a flattened data object with deep paths flattened into `a.c.b` format. */ export type LeafDataProp<T extends Data> = { readonly [K in DataKey<T>]: (T[K] extends Data ? LeafDataProp<T[K]> : readonly [null, T[K]]) extends infer E ? E extends readonly [infer KK, infer VV] ? readonly [KK extends string ? `${K}.${KK}` : K, VV] : never : never; }[DataKey<T>]; /** * Typed path tuple for only leaf nodes of a data object. * i.e. `LeafPath<{ a: number, b: { c: string } }>` produces `readonly ["a"] | readonly ["b", "c"]` */ export type LeafDataSegments<T extends Data> = { readonly [K in DataKey<T>]: T[K] extends Data ? readonly [K, ...LeafDataSegments<T[K]>] : readonly [K]; }[DataKey<T>]; /** * Object with one level of data nested beneath each prop. * i.e. `{ A: { a: number }, B: { b: string } }` */ export type NestedData = { readonly [key: string]: Data; }; /** * Helper type to flatten one level of nested data into a single flat `Data` type. * i.e. `FlattenData<{ A: { a: number }, B: { b: string } }>` produces `{ a: number, b: string }` */ export type FlatData<T extends NestedData> = Resolve<UnionToIntersection<T[keyof T]>>; /** Is an unknown value a data object? */ export declare function isData(value: unknown): value is Data; /** Assert that an unknown value is a data object. */ export declare function assertData(value: unknown, caller?: AnyCaller): asserts value is Data; /** Is an unknown value the key for an own prop of a data object. */ export declare const isDataProp: <T extends Data>(data: T, key: unknown) => key is DataKey<T>; /** Assert that an unknown value is the key for an own prop of a data object. */ export declare function assertDataProp<T extends Data>(data: T, key: unknown, caller?: AnyCaller): asserts key is DataKey<T>; /** Get the props of a data object as a set of entries. */ export declare function getDataProps<T extends Data>(data: T): ImmutableArray<DataProp<T>>; export declare function getDataProps<T extends Data>(data: T | Partial<T>): ImmutableArray<DataProp<T>>; /** Get the keys of a data object as an array. */ export declare function getDataKeys<T extends Data>(data: T): ImmutableArray<DataKey<T>>; export declare function getDataKeys<T extends Data>(data: T | Partial<T>): ImmutableArray<DataKey<T>>; /** * Split a dotted path into a data path segments. * @example splitDataKey<{ a: { b: number } }>("a.b"); // Returns `["a", "b"]` */ export declare function splitDataPath<T extends Data>(path: BranchDataPath<T> | BranchDataSegments<T>): BranchDataSegments<T>; export declare function splitDataPath<T extends Data>(path: LeafDataPath<T> | LeafDataSegments<T>): LeafDataSegments<T>; export declare function splitDataPath(path: string | Segments): Segments; /** * Join a set of data path segments into data path tuple back into a dotted path. * @example joinDataKey<{ a: { b: number } }>(["a", "b"]); // Returns `"a.b"` */ export declare function joinDataPath<T extends Data>(path: BranchDataSegments<T> | BranchDataPath<T>): BranchDataPath<T>; export declare function joinDataPath<T extends Data>(path: LeafDataSegments<T> | LeafDataPath<T>): LeafDataPath<T>; export declare function joinDataPath(path: Segments | string): string; /** * Get an optional (possibly deep) prop from a data object, or `undefined` if it doesn't exist. * @example getDataProp<{ a: { b: number } }>({ a: { b: 123 } }, ["a", "b"]); // Returns `123` * @example getDataProp<{ a: { b: number } }>({ a: { b: 123 } }, "a.b"); // Returns `123` * @example getDataProp({ a: { b: 123 } } as Data, "x.y.z"); // Returns `undefined` */ export declare function getDataProp<T extends Data, K extends BranchDataPath<T>>(data: T, path: K): BranchData<T>[K]; export declare function getDataProp<T extends Data, K extends BranchDataPath<T>>(data: DeepPartial<T>, path: K): BranchData<T>[K] | undefined; export declare function getDataProp(data: Data, path: string | Segments): unknown;