Projection
array -> { ... } maps each element of an array to a new object shape. It is Typeflow's replacement for Array.prototype.map.
| Operator | Effect |
|---|---|
-> | map each element of an array to a new object shape |
$ | the current element (required for scalar arrays) |
-> alias | name the current element |
-> alias, index | name the element and its 0-based position |
$root, $parent | reach the input or the enclosing element |
-> : map each element of an array to a new object shape
Inside the projection body, bare identifiers resolve on the current element, and $ is the element itself. Postfix constructs chain — filter first (arr[bool] -> { ... }), then reshape the survivors.
views: user.labels -> {
label: upper(name),
active: active,
original: $,
}$ : the current element (required for scalar arrays)
For arrays of scalars, $ is the only way to reference the element.
detailed: stats.scores -> {
value: $,
doubled: $ * 2,
passed: $ >= 12,
}-> alias : name the current element
Since bare identifiers resolve on the current element, an element field shadows an outer variable of the same name — including the input. Give the element an explicit alias with -> alias { ... } and reference its fields through the alias; bare names, the alias, and $ all stay available, and the parent scope is never shadowed.
views: user.labels -> l {
label: upper(l.name),
active: l.active,
original: $,
}-> alias, index : name the element and its 0-based position
A second binder captures the element’s 0-based index in the array as a number — useful for ordinals or sequential ids. Both binders are named: -> el, i { ... }. The element also stays reachable as $ and through bare fields, so name the element _ when you only need the index.
ranked: board.scores -> s, i {
rank: i + 1,
score: s,
}$root, $parent : reach the input or the enclosing element
When you stay in the bare-field style, $root reaches the input through any level of nesting — even past a field that shadows it — and $parent reaches the enclosing projection or filter element. Chain it — $parent.$parent climbs two levels, $parent.$parent.$parent three — and a chain that climbs past every enclosing element lands on the input. At the top level (a single ->), the element has no enclosing element, so $parent is already the input, the same as $root.
regions: doc.regions -> {
code: code,
cities: cities -> {
districts: districts -> {
name: name,
city: $parent.name,
region: $parent.$parent.code,
},
},
}Playground
{ "activeViews": [ { "label": "FOUNDER", "slug": "founder", "org": "acme" }, { "label": "MATH", "slug": "math", "org": "acme" } ] }
{
activeViews: { label: string; slug: string; org: string }[];
}Projection bodies are full map bodies: they can nest objects, use conditionals, call functions — anything a top-level field can do. Use an alias (-> l { ... }) whenever an element field would shadow a name you still need, and $root / $parent to reach outward from inside the body.