Skip to content

API cheatsheet

ExpressionReturnsDescription
path<T>()Path<T, T>Root path with zero segments
path((p: T) => p.a.b)Path<T, V>Lambda form — annotate the parameter to infer both T and V
path<T, V>((p) => p.a.b)Path<T, V>Both generics explicit
path(base, (p) => p.c)Path<T, V>Extend an existing base path
unsafePath<T>("a.b")Path<T, unknown>From a raw dot-notation string
unsafePath<T, V>("a.b")Path<T, V>From a raw string with explicit leaf type
PropertyTypeDescription
path.$stringDot-notation string ("profile.firstName")
path.segmentsreadonly (string | number)[]Array of segments
path.lengthnumberNumber of segments
path.fn(data: T) => V | undefinedStable accessor — same reference on every access

On template paths, fn returns V[] instead of V | undefined.

MethodSignatureDescription
.get(data)(T) => V | undefinedRead value at path; returns undefined if any segment is missing
.set(data, value)(T, V) => TImmutable write — returns a structural clone
.update(data, fn)(T, (V | undefined) => V) => TRead-modify-write in one call

On template paths, .get() returns V[] and .set() / .update() apply to all matched paths.

MethodSignatureDescription
.to(lambda | path)Path<T, U> | TemplatePath<T, U>Extend the path. Result is a TemplatePath when relative carries wildcard sentinels (built via .each()/.deep()); otherwise Path
.parent()Path<T, unknown> | nullRemove the last segment; null at root
.merge(other)Path<T, U> | TemplatePath<T, U>Overlap-aware concatenation. Result is a TemplatePath when other carries wildcard sentinels
.subtract(prefix)Path<U, V> | nullRemove a prefix; null if prefix doesn’t match
.slice(start?, end?)Path<T, unknown>Slice segments like Array.prototype.slice
MethodSignatureDescription
.each()TemplatePath<T, CollectionItem<V>>Wildcard over every key of the current collection
.each(expr)TemplatePath<T, U>Wildcard then navigate each item
.deep()TemplatePath<T, V>Recursive descent wildcard
.deep(expr)TemplatePath<T, U>Recursive descent then navigate
.expand(data)Path<T, V>[]Resolve template to concrete paths that exist in data

each and deep are only available when V is not a primitive type.

Template paths also support .to() and .merge() — both return TemplatePath<T, U> because this already carries wildcards.

On a TemplatePath, the structural methods are widened to keep wildcards when present:

Method on TemplatePathSignature
.parent()Path<T, unknown> | TemplatePath<T, unknown> | null
.slice(start?, end?)Path<T, unknown> | TemplatePath<T, unknown>
.subtract(prefix)Path<U, V> | TemplatePath<U, V> | null

If the resulting segments still contain * / **, you get a TemplatePath (so .get() continues to expand matches); otherwise a concrete Path. Narrow at the call site if you need to disambiguate.

MethodSignatureDescription
.startsWith(other)booleantrue if this path begins with every segment of other
.covers(other)booleantrue if this path is a prefix of other (this location covers other in the data tree)
.equals(other)booleantrue if both paths have identical segments
.match(other)MatchResult | nullStructural relationship, or null if none

All relational and algebra methods accept a ResolvablePath<T> argument, which means any of:

  • A Path<T> or TemplatePath<T> instance
  • A lambda (proxy: T) => unknown (evaluated once against a proxy)
  • An object with a segments property