Templates and Wildcards
Relevant source files
The following files were used as context for generating this wiki page:
Templates and Wildcards in data-path allow developers to define paths that match multiple elements within a data structure simultaneously. While a standard Path identifies a single specific location (e.g., users.0.name), a TemplatePath uses wildcards like * (single-level) or ** (multi-level deep scan) to target groups of properties or array items. This functionality is essential for bulk data operations, such as extracting all values in a collection or performing immutable updates across multiple nodes in one operation.
Sources: README.md:144-150, src/types.ts:257-270
Core Wildcards
The system utilizes two primary wildcard constants to define template behavior:
- Single-level Wildcard (
*): Represented by theWILDCARDconstant, this matches all keys in a Record or all indices in an Array at a specific depth. - Deep Wildcard (
**): Represented by theDEEP_WILDCARDconstant, this matches properties at any depth within the object hierarchy.
Sources: src/impl/template-path-impl.ts:1-10, src/tests/templates.spec.ts:22-30, src/tests/templates.spec.ts:54-62
Template Path Architecture
The TemplatePath interface extends the foundational BasePath but specializes in handling non-deterministic resolutions. It introduces the expand(data) method, which resolves a wildcard-based path into an array of concrete, absolute Path objects based on the actual shape of the provided data.
Class Hierarchy and Relationships
The following diagram illustrates the inheritance and relationship between standard paths and template paths.
Key Methods
.each() and .deep()
These methods convert a standard path into a template path. They are restricted to non-primitive values; if a path resolves to a string or number, these methods are excluded from the type system to prevent invalid traversals.
| Method | Syntax | Behavior |
|---|---|---|
each | path.each(expr?) | Appends * to segments. Optionally extends the path via the lambda expression. |
deep | path.deep(expr?) | Appends ** to segments. Performs a recursive scan for the specified property. |
Sources: src/types.ts:271-294, src/impl/template-path-impl.ts:23-40, src/impl/template-path-impl.ts:49-65
.expand() Logic
The expand method implements a recursive "walk" through the data structure. It interprets wildcards to find all valid concrete paths that satisfy the template.
Data Operations
Templates redefine standard data access methods to handle multiple values:
- get(data): Returns an array of values (
V[]) collected from all matched paths. - set(data, value): Performs a sequence of immutable updates. It first expands the template into concrete paths and then iteratively applies
.set()to the data, returning a single updated structural clone. - fn: Returns an accessor function
(data: T) => V[]suitable for use in.map()or.filter().
Sources: src/impl/template-path-impl.ts:131-158, src/tests/templates.spec.ts:130-150
Usage Examples
Single Wildcard Expansion
const tmpl = path((p: User) => p.items).each(i => i.name);
// tmpl.$ is "items.*.name"
const paths = tmpl.expand({ items: [{name: 'A'}, {name: 'B'}] });
// returns [Path("items.0.name"), Path("items.1.name")]
Sources: src/tests/templates.spec.ts:75-87
Deep Scanning
const deepIds = path<AppData>().deep(node => node.id);
// targets "**.id" at any depth within the object tree
Sources: README.md:167-168, src/impl/template-path-impl.ts:49-55
Summary
Templates and Wildcards extend the data-path library from specific property targeting to broad pattern matching. By leveraging TemplatePathImpl, the system provides a type-safe mechanism to resolve dynamic data structures into concrete paths, enabling efficient bulk reads and immutable writes across collections and deeply nested trees.
Sources: src/impl/template-path-impl.ts, src/types.ts:257-270