shelving/util/datamodule

The Data type and its helpers are the foundation of Shelving's data layer. A Data object is a plain Record<string, unknown> — a JSON-safe object with no class prototype. Every database document, query result, and schema-validated value is a Data.

  • Deep paths use dot notation: "user.address.city" or the equivalent tuple ["user", "address", "city"].
  • getDataProp() never throws — it returns undefined for missing or non-object intermediate nodes.
  • splitDataPath() / joinDataPath() are inverses of each other; both accept either form so you can freely convert.

Usage

Type guards and assertions

ts
import { isData, assertData } from "shelving/util";

isData({ name: "Alice" }); // true
isData(new Map());         // false
isData(null);              // false

assertData(value); // throws RequiredError if value is not a plain object

Checking and accessing props

ts
import { isDataProp, assertDataProp, getDataProps, getDataKeys } from "shelving/util";

const doc = { name: "Alice", age: 30 };

isDataProp(doc, "name");  // true
isDataProp(doc, "email"); // false

getDataKeys(doc);  // ["name", "age"]
getDataProps(doc); // [["name", "Alice"], ["age", 30]]

Reading nested values with dot paths

ts
import { getDataProp, splitDataPath, joinDataPath } from "shelving/util";

const doc = { user: { address: { city: "London" } } };

getDataProp(doc, "user.address.city");           // "London"
getDataProp(doc, ["user", "address", "city"]);   // "London"
getDataProp(doc, "user.missing.key");            // undefined

splitDataPath("user.address.city"); // ["user", "address", "city"]
joinDataPath(["user", "address", "city"]); // "user.address.city"

TypeScript utility types

data.ts exports a rich set of mapped types for working with typed data objects:

TypePurpose
DataBase plain-object type
PartialData<T>All keys optional
DataKey<T>Union of string keys
DataValue<T>Union of value types
LeafData<T>Flat object with only leaf paths
LeafDataPath<T>Union of dotted leaf paths
BranchData<T>Flat object including intermediate object paths
NestedDataOne level of nesting: { Group: { key: value } }
FlatData<T>Flattened union of nested data
ts
import type { Data, LeafDataPath } from "shelving/util";

type Profile = { name: string; address: { city: string } };
type ProfileLeafPaths = LeafDataPath<Profile>; // "name" | "address.city"

Functions

Go

isData()function

Is an unknown value a data object?

isData(value: unknown): value is Data
Go

assertData()function

Assert that an unknown value is a data object.

assertData(value: unknown, caller: AnyCaller = assertData): asserts value is Data
Go

isDataProp()function

Is an unknown value the key for an own prop of a data object?

isDataProp(data: T, key: unknown): key is DataKey<T>
Go

assertDataProp()function

Assert that an unknown value is the key for an own prop of a data object.

assertDataProp(data: T, key: unknown, caller: AnyCaller = assertDataProp): asserts key is DataKey<T>
Go

getDataProps()function

Get the props of a data object as a set of entries.

getDataProps(data: T): ImmutableArray<DataProp<T>>
getDataProps(data: T | Partial<T>): ImmutableArray<DataProp<T>>
Go

getDataKeys()function

Get the keys of a data object as an array.

getDataKeys(data: T): ImmutableArray<DataKey<T>>
getDataKeys(data: T | Partial<T>): ImmutableArray<DataKey<T>>
Go

splitDataPath()function

Split a dotted path into data path segments.

splitDataPath(path: BranchDataPath<T> | BranchDataSegments<T>): BranchDataSegments<T>
splitDataPath(path: LeafDataPath<T> | LeafDataSegments<T>): LeafDataSegments<T>
splitDataPath(path: string | Segments): Segments
Go

joinDataPath()function

Join a set of data path segments into a dotted path string.

joinDataPath(path: BranchDataSegments<T> | BranchDataPath<T>): BranchDataPath<T>
joinDataPath(path: LeafDataSegments<T> | LeafDataPath<T>): LeafDataPath<T>
joinDataPath(path: Segments | string): string
Go

getDataProp()function

Get an optional (possibly deep) prop from a data object, or undefined if it doesn't exist.

getDataProp(data: T, path: K): BranchData<T>[K]
getDataProp(data: DeepPartial<T>, path: K): BranchData<T>[K] | undefined
getDataProp(data: Data, path: string | Segments): unknown

Types

Go

Datatype

Data object — a plain object with string keys and unknown values.

{ readonly [K in string]: unknown }
Go

PartialDatatype

Partial data object (values can be explicitly undefined).

{ readonly [K in keyof T]?: T[K] | undefined }
Go

DataKeytype

Helper type to get the key for a data object prop.

keyof T & string
Go

DataValuetype

Helper type to get the value for a data object prop.

T[DataKey<T>]
Go

DataProptype

Helper type to get a prop for a data object. i.e. DataProp<{ a: number }> produces readonly ["a", number"]

{
	readonly [K in DataKey<T>]: readonly [K, T[K]];
}[DataKey<T>]
Go

BranchDatatype

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 }

EntryObject<BranchDataProp<T>>
Go

BranchDataPathtype

Helper type to get the path for a flattened data object with deep paths flattened into a.c.b format.

BranchDataProp<T>[0]
Go

BranchDataValuetype

Helper type to get the value for a flattened data object with deep paths flattened into a.c.b format.

BranchDataProp<T>[1]
Go

BranchDataProptype

Helper type to get the prop for a flattened data object with deep paths flattened into a.c.b format.

{
	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>]
Go

BranchDataSegmentstype

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"]

{
	readonly [K in DataKey<T>]: T[K] extends Data ? readonly [K] | readonly [K, ...BranchDataSegments<T[K]>] : readonly [K];
}[DataKey<T>]
Go

LeafDatatype

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 }

EntryObject<LeafDataProp<T>>
Go

LeafDataPathtype

Helper type to get the leaf paths for a flattened data object with deep paths flattened into a.c.b format.

LeafDataProp<T>[0]
Go

LeafDataValuetype

Helper type to get the leaf values for a flattened data object with deep paths flattened into a.c.b format.

LeafDataProp<T>[1]
Go

LeafDataProptype

Helper type to get the leaf props for a flattened data object with deep paths flattened into a.c.b format.

{
	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>]
Go

LeafDataSegmentstype

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"]

{
	readonly [K in DataKey<T>]: T[K] extends Data ? readonly [K, ...LeafDataSegments<T[K]>] : readonly [K];
}[DataKey<T>]
Go

NestedDatatype

Object with one level of data nested beneath each prop. i.e. { A: { a: number }, B: { b: string } }

{
	readonly [key: string]: Data;
}
Go

FlatDatatype

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 }

Resolve<UnionToIntersection<T[keyof T]>>