Shape nodes reference#
A compiled shape is a tree of nodes. You rarely touch nodes directly, but
custom validators receive an Update and a State, and introspection returns
the node tree.
The node#
shape.node() (TS) / s.Node() (Go) returns the root node. Key TS node fields:
| Field | Meaning |
|---|---|
$ |
marker that the value is a compiled node (Shape.isShape tests the same marker on a shape) |
t |
type name (string, number, object, array, any, list, check, …) |
d |
depth in the tree (the root is 0) |
v |
defining value |
f |
default value, if any |
n |
number of keys in the defining value |
r |
required |
p |
skippable (optional, no default injection) |
c |
default child shape (open objects / arrays) |
k |
final property keys, in order |
e |
whether match failures are reported as errors (false under Ignore) |
b / a |
before / after validator lists |
u / m |
user data / metadata—u.nullable for Nullable, u.empty for Empty, u.list and u.discriminated for a composition's branches, m.description for Describe, m.rest for Rest |
z |
custom (Fault) message |
The remaining keys are the chainable builder methods (node.Min(2)).
Go stores the equivalent on an unexported node; use s.Spec() for a
JSON-friendly view, or Node.Inner() / Node.Kind() for a compiled builder.
Update—what a validator returns through#
A custom validator (Check/Before/After) fills in an update:
TS Update |
Go Update |
Meaning |
|---|---|---|
done |
Done |
stop running further checks on this node |
val |
Val (+ HasVal) |
replace the value |
uval |
n/a | replace with undefined/NaN (TS) |
node |
Node |
swap in a different node (used by Refer) |
type |
n/a | override the type (TS) |
err |
Err |
a message string, an error object, or a list of them |
why |
Why |
why-code for the failure |
fatal |
n/a | force the error to be reported even under Ignore (TS) |
nI / sI / pI |
n/a | traversal cursor overrides; internal (TS) |
Return true to pass, false to fail. Setting update.err implies failure,
and a failed check also sets done unless the validator set it itself, so the
node's structural check is skipped—the other validators on the node still run.
A Check validator is not called for an absent (undefined) value; Before
and After validators are.
State—the current cursor#
Read-only context for a validator:
TS State |
Go State |
Meaning |
|---|---|---|
val |
Value |
current value |
valType |
n/a | the type name of val as Shape reads it (TS) |
key |
Key |
current key/index |
path |
Path |
path stack from the root |
node |
Node |
current node |
parent |
Parent |
parent container |
root |
n/a | the value passed to the shape (TS) |
ancestors |
n/a | the nodes from the root down to the current one (TS) |
match |
Match |
true during match/Match (no mutation) |
fromDflt |
n/a | true when val was just injected from a default (TS) |
ctx |
Ctx |
the validation context (your custom fields live here) |
curerr |
n/a | errors accumulated for the current node (TS) |
err |
n/a | errors accumulated for the whole run so far (TS) |
Example#
const { Shape, Check } = require('shape')
Shape({
slug: Check((val, update, state) => {
if (typeof val !== 'string') { update.err = 'slug must be a string'; return false }
update.val = val.toLowerCase() // normalize
return true
}),
})