Bindings
let name = expr names a subexpression inside a block so it can be reused — Typeflow's take on JSONata's $x := .... Bindings are immutable and don't appear in the output; they exist only to avoid repetition and make intent clear.
| Operator | Effect |
|---|---|
let name = expr | name a subexpression for reuse |
block scope | bindings reach into nested blocks |
declaration order | a binding sees only earlier bindings |
let name = expr : name a subexpression for reuse
A let binding is visible to every property of its block. It computes once and reads as an ordinary identifier — total: base + tax instead of repeating the underlying paths.
let base = order.subtotal
let tax = base * 0.2
total: base + taxblock scope : bindings reach into nested blocks
A binding is scoped to its block and every nested block within it, so a projection body (or a nested object) can add its own bindings and still see the outer ones.
lines: order.items -> {
let net = price * qty,
net: net,
withTax: net * 1.2,
}declaration order : a binding sees only earlier bindings
All of a block’s properties may use any of its bindings, but a binding may only reference bindings declared before it — forward and self references are rejected (TF2001), so mappings stay terminating. Two bindings with the same name in one block is an error (TF2018).
let base = order.subtotal
let withTax = base * 1.2Playground
{ "subtotal": 100, "tax": 20, "total": 120, "lines": [ { "net": 20, "withTax": 24 }, { "net": 20, "withTax": 24 } ] }
{
subtotal: number;
tax: number;
total: number;
lines: { net: number; withTax: number }[];
}For logic you want to reuse across mappings — not just within one block — reach for a function instead: fn for pure mapping-language helpers, use for typed host functions.