Templates
A template path contains one or more wildcards (* or **). It targets multiple values at once rather than a single leaf. Template paths are created with .each() and .deep().
each — single-level wildcard
Section titled “each — single-level wildcard”.each() appends a * wildcard that matches every key of the current collection.
import { path } from "data-path";
type AppData = { users: Array<{ name: string; active: boolean }> };
const allUsersPath = path((d: AppData) => d.users).each();allUsersPath.$ // "users.*"
const allNamesPath = path((d: AppData) => d.users).each((u) => u.name);allNamesPath.$ // "users.*.name"The optional expression argument navigates from each matched item. Without it, .each() targets the items themselves.
deep — recursive wildcard
Section titled “deep — recursive wildcard”.deep() appends a ** wildcard that matches the current value and all descendants recursively.
type Tree = { label: string; children?: Tree[] };
const allLabelsPath = path((t: Tree) => t).deep((n) => n.label);allLabelsPath.$ // "**.label"** tries to match the rest of the pattern at the current depth first, then recurses into every child.
get — bulk read
Section titled “get — bulk read”On a template path, .get() returns V[] (an array of all matched values):
const data: AppData = { users: [{ name: "Alice", active: true }, { name: "Bob", active: false }],};
allNamesPath.get(data) // ["Alice", "Bob"]set — bulk write
Section titled “set — bulk write”.set(data, value) applies the same value to every matched path:
const anonymized = allNamesPath.set(data, "Hidden");anonymized.users[0].name // "Hidden"anonymized.users[1].name // "Hidden"data.users[0].name // "Alice" — original unchangedupdate — per-item transform
Section titled “update — per-item transform”.update(data, fn) calls the updater once per matched path, with the current value at that path:
const uppercased = allNamesPath.update(data, (name) => (name ?? "").toUpperCase());uppercased.users[0].name // "ALICE"uppercased.users[1].name // "BOB"Each update is applied to the result of the previous one — later paths see the changes from earlier paths.
expand — resolve to concrete paths
Section titled “expand — resolve to concrete paths”.expand(data) walks the data and returns one Path<T, V> per matched leaf:
const paths = allNamesPath.expand(data);paths[0].$ // "users.0.name"paths[1].$ // "users.1.name"expand only visits keys that exist in the data. Missing intermediates are skipped, so .expand() on sparse data returns fewer paths than the structure implies.
Chaining wildcards
Section titled “Chaining wildcards”Multiple .each() and .deep() calls compose naturally:
type Org = { teams: Array<{ members: Array<{ email: string }> }> };
const allEmailsPath = path((o: Org) => o.teams) .each((t) => t.members) .each((m) => m.email);
allEmailsPath.$ // "teams.*.members.*.email"fn on template paths
Section titled “fn on template paths”.fn on a template path returns (data: T) => V[], suitable for .map():
const allNames = teamList.map(allNamesPath.fn); // string[][]See also
Section titled “See also”- Data access —
get,set,updateon concrete paths - Runtime Variables — dynamic indices as an alternative to wildcards