Skip to content

Projection ​

array -> { ... } maps each element of an array to a new object shape. It is Typeflow's replacement for Array.prototype.map.

OperatorEffect
->map each element of an array to a new object shape
$the current element (required for scalar arrays)
-> aliasname the current element
-> alias, indexname the element and its 0-based position
$root, $parentreach 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.

typeflow
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.

typeflow
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.

typeflow
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.

typeflow
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.

typeflow
regions: doc.regions -> {
  code: code,
  cities: cities -> {
    districts: districts -> {
      name: name,
      city: $parent.name,
      region: $parent.$parent.code,
    },
  },
}

Playground ​

Mapping✔Playground ↗
Input JSON
Output
{
  "activeViews": [
    {
      "label": "FOUNDER",
      "slug": "founder",
      "org": "acme"
    },
    {
      "label": "MATH",
      "slug": "math",
      "org": "acme"
    }
  ]
}
inferred type{ 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.

Released under the MIT License.