# TypeScript Transformers Target Pattern Language Specification **Status:** Candidate v1 (normative target language for current hardening phase)\ **Package:** `@commonfabric/ts-transformers`\ **Related:** - `docs/specs/ts-transformer/ts_transformers_current_behavior_spec.md` - `docs/specs/ts-transformer/ts_transformers_lowering_contract.md` - `docs/specs/ts-transformer/ts_transformers_goals.md` ## 1. Purpose This document defines the **intended authored pattern language** that the transformer pipeline should support. It is not a file-by-file description of the current implementation. Instead, it answers: 1. what authors should be allowed to write 2. which constructs are first-class parts of the language 3. which constructs are merely tolerated compatibility behavior 4. which constructs are outside the language and should diagnose clearly If this document and the current implementation disagree on a supported or unsupported construct family, treat the implementation as needing correction or an explicitly recorded follow-up in the design-deltas/current-behavior docs. Do not silently let implementation accident become language policy. ## 2. Scope This v1 draft focuses on the **reactive expression language inside patterns**: - supported lowered value-expression sites (`jsx-expression`, `return-expression`, `variable-initializer`, `call-argument`, `object-property`, `array-element`) - helper-owned control flow (`ifElse`, `when`, `unless`) - collection operators over reactive receivers - direct reactive property/element access - pattern-body expression forms that interact with ownership-first lowering It does **not** attempt to restate every validation rule in the package. The existing current-behavior spec remains the descriptive inventory for those details. ## 3. Status Labels Each construct family is classified as one of: - **Supported** - first-class part of the intended language - **Compatibility-only** - tolerated behavior for existing code, but not something we want to bless as a core target-language construct - **Unsupported** - outside the intended language; should fail clearly or remain explicitly out of scope ## 4. Core Language Matrix | Construct family | Status | Intended meaning | | --- | --- | --- | | Reactive property access in JSX or helper-owned expressions | Supported | Authored reactive reads like `state.user.name` should remain natural and lower to explicit reactive access as needed | | Reactive element access with static or known-symbol keys | Supported | Forms like `items[0]`, `item[NAME]`, `state["foo"]` should lower predictably when the access path is statically representable | | Reactive ternary control flow in supported lowered value-expression sites | Supported | Authored `cond ? x : y` should preserve JavaScript branch meaning in JSX, top-level pattern-body value sites, and callback-local values inside supported collection callbacks | | Reactive logical control flow in supported lowered pattern-owned expression sites (`&&`, `||`, `??`) | Supported | Reactive short-circuiting should preserve authored JavaScript meaning where the expression-site policy admits lowering | | Authored helper control flow (`ifElse`, `when`, `unless`) | Supported | These are first-class reactive control-flow forms, not mere implementation helpers | | `map` / `filter` / `flatMap` on reactive receivers in pattern-facing contexts | Supported | These operators are core language forms and may be structurally rewritten to explicit reactive collection operators | | Callback-local plain JS arrays in rewritten callbacks | Supported | Plain JS arrays inside callbacks stay plain; they are not implicitly promoted into pattern-owned array operators | | Direct JSX sink chains over structural array results | Supported | Terminal sink chains like `.filter(...).join(", ")` and ordinary receiver-method chains above that sink are valid JSX expression forms | | Receiver-method calls inside JSX expressions, explicit computation callbacks, or authored helper control flow | Supported | Receiver methods are valid in local authored expression contexts such as JSX interpolation, `computed` / `action` / `lift` / `handler` callbacks, and helper control flow branches like `ifElse(show, name.trim(), "fallback")` | | Event-handler JSX attributes | Supported | Event handlers form an explicit callback boundary; they are part of the language but not part of ordinary expression-site lowering | | Dynamic key access inside JSX expressions, explicit computation callbacks, supported collection callbacks, or structural binding forms | Supported | Dynamic access like `selectedScopes[key]` is valid in local authored expression contexts or in binding forms that preserve the dynamic key directly | | Bare dynamic key access in top-level pattern-facing code | Unsupported | Forms like `input[key]` as a direct top-level pattern-body traversal are outside the intended declarative language and should move into JSX, an explicit computation callback, a supported collection callback, or a structural binding form | | Cell-style `.key(...)` traversal on explicitly cell-like values | Supported | When the authored value is truly `Cell`/`Writable`/`Stream`-like, `.key(...)` remains part of that value's direct API rather than an implementation artifact | | Cell-style `.get()` reads on explicitly cell-like values inside JSX expressions, authored helper control flow, or explicit computation callbacks | Supported | Eager cell reads remain valid when authored in JSX interpolation, helper control flow such as `ifElse` / `when` / `unless`, and explicit computation callbacks such as `computed`, `action`, `lift`, and `handler` | | Foreign callback / imperative container roots in JSX | Unsupported | Shapes like `[0, 1].forEach(() => list.map(...))` are not part of the intended reactive language core and should move into supported value expressions, wrappers, or helpers | | Residual callback-container pass-through behavior for invalid programs | Compatibility-only | Some invalid callback-container shapes may still survive as plain JS in current emitted output, but that is residual implementation behavior rather than supported language policy | | Optional-call on reactive receivers | Unsupported | Optional-call forms are outside the intended language because they are difficult to lower without semantic ambiguity | | Direct non-JSX receiver-method calls on reactive values in top-level pattern-body expression sites | Supported | Value-like receiver-method roots at top-level object-property, call-argument, variable-initializer, array-element, or return-expression sites lower to derived local value expressions | | Direct receiver-method roots inside supported collection callbacks | Supported | Callback-local value-like receiver-method roots lower to callback-local lift-applied computations instead of remaining raw or requiring manual wrapper calls | | Direct top-level `.get()` reads in pattern-owned reactive context | Unsupported | Even on true cell-like values, eager `.get()` reads should move into JSX or an explicit computation callback such as `computed`, `action`, `lift`, or `handler` rather than living directly in the top-level declarative pattern body | | `.get()` on ordinary opaque/reactive values | Unsupported | Pattern inputs, `computed` results, `lift` results, and other ordinary reactive values should be read directly rather than through `.get()` | | Statement-boundary imperative constructs in top-level pattern-owned code (`let`, loops, function creation, early return) | Unsupported | Top-level pattern context is intentionally declarative; imperative statement structure belongs in explicit callback bodies such as `computed`, `action`, `lift`, or `handler` | ## 4.1 Authoring Context Guide The matrix above is the policy summary. This section states the same boundary in author-facing terms: **what kinds of expressions belong in each authored context.** ### Supported Lowered Value-Expression Sites The shared lowering model starts from a small set of recognized authored container kinds: - `jsx-expression` - `return-expression` - `variable-initializer` - `call-argument` - `object-property` - `array-element` Those container kinds appear to authors in three main buckets: 1. JSX expressions 2. top-level pattern-body value-expression sites such as returned object property values, variable initializers, call arguments, array elements, and direct function return expressions 3. callback-local value-expression sites inside supported reactive collection callbacks Explicit computation callbacks such as `computed`, `action`, `lift`, and `handler` are important boundaries, but their bodies are **not** blanket "lower everything here" regions. The shared container list above does not imply that nested compute-context JSX/control-flow receives pattern-context lowering; current-main behavior preserves authored JavaScript control flow there. ### Top-Level Pattern Body The top-level pattern body should stay declarative. **Good here** ```ts // Shown for illustration only. pattern(({ items, show }) => ({ upper: items[0].name.toUpperCase(), title: show ? "Visible" : "Hidden", visibleCount: ifElse(show, items.length, 0), [UI]:
{items.map((item) => item.name)}
, })); ``` **Move elsewhere** ```ts // Shown for illustration only. pattern(({ user, count }) => ({ value: count.get(), })); ``` Why: - top-level pattern-body value-expression sites participate in the shared lowering model - top-level helper control flow is part of the language - top-level receiver-method roots are supported at lowerable non-JSX expression sites - eager `.get()` reads still move into JSX, authored helper control flow, or an explicit computation callback ### JSX Expressions JSX is the main local reactive expression context. **Good here** ```tsx // Shown as JSX element children.
{user.name.toUpperCase()} {selectedScopes[key]} {ifElse(show, count.get(), 0)} {items.filter((item) => item.visible).join(", ")}
``` **Unsupported here** ```tsx // Shown for illustration only.
{[0, 1].forEach(() => list.map((item) => item))}
``` Why: - JSX supports local reactive reads, control flow, receiver methods, supported collection callbacks, and true-cell eager reads - JSX does not bless foreign imperative callback containers as language forms ### Explicit Computation Callbacks `computed`, `action`, `lift`, and `handler` callbacks are explicit imperative/value-computation boundaries. **Good here** ```ts // Shown inside a pattern body. computed(() => input[key]) computed(() => count.get()) action(() => state.name.trim()) ``` **Still unsupported here** ```ts // Shown inside a pattern body. computed(() => derivedValue.get()) ``` Why: - dynamic access, receiver methods, and true-cell eager reads are valid here - `.get()` on ordinary opaque/reactive values is still not part of the language, even inside a computation callback ### Event Handler JSX Attributes Event handlers are an explicit callback boundary for imperative UI logic. **Good here** ```tsx // Shown as JSX element children.