Skip to content

feat(nanoviews): add as_ to hand a row through a transform - #207

Merged
dangreen merged 1 commit into
mainfrom
feat/nanoviews-as-row-transform
Aug 20, 2026
Merged

feat(nanoviews): add as_ to hand a row through a transform#207
dangreen merged 1 commit into
mainfrom
feat/nanoviews-as-row-transform

Conversation

@dangreen

Copy link
Copy Markdown
Member

A row that needs a transform cannot be an expression. From this repo's own weather example:

for_(props.$suggestions, trackBy('label'))(
  ($city, index) => {
    const city = record($city)

    return li({ role: 'presentation' })(
      // …
    )
  }
)

Four lines of scaffolding — the braces, the const, the blank line, the return — around a body that is one expression. as_ gives the expression back:

for_(props.$suggestions, trackBy('label'))(
  as_(record, (city, index) => li({ role: 'presentation' })(
    // …
  ))
)

The whole thing

/* @__NO_SIDE_EFFECTS__ */
export function as_<Item, Index, Value>(
  as: (item: Item) => Value,
  each_: (value: Value, index: Index) => Child
) {
  return (item: Item, index: Index) => each_(as(item), index)
}

The transform is any function of the row, not record specifically. The row and index types are deliberately unconstrained, so one signature serves all three shapes for_ hands out — the writable row, the readable one, and the static one.

The thing that decided this was worth having is that inference survives. record is itself generic, so the row type has to flow through two generic calls in a row before it reaches the callback. It does, with no type arguments written anywhere:

for_($cities, trackById)(
  as_(record, (city, index) => li()(city.$name, ':', index))
)

city.$name is WritableSignal<string> there — checked by giving it to a number and reading the error, not by looking at it.

Tests

Two, both rendering for real: a row goes through record, the index arrives second, and a change to the array repaints; and — the one that matters — a row transformed by as_ is still the row, so normalising $player.$name inside the body writes back into the items array.

Size

+17 B gzip on all publics, and nothing on the average-usage bundle: as_ is a separate export, so a consumer who does not import it does not pay for it. One brotli pin moves up a step; the rest stay.

A row that needs `record` has to be a block: a `const` for the transformed row, a blank line, a `return`, and the braces around them. `as_(record, $player => li()($player.$name))` makes it an expression again.

It is not about `record` - the transform is any function of the row, and the row types are left unconstrained so one signature serves the readable, writable and static forms. The transformed row stays the row: a write through it still reaches the items array.
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.33%. Comparing base (215f157) to head (8f98203).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #207      +/-   ##
==========================================
+ Coverage   85.29%   85.33%   +0.03%     
==========================================
  Files         139      139              
  Lines        3142     3143       +1     
  Branches      591      591              
==========================================
+ Hits         2680     2682       +2     
  Misses        332      332              
+ Partials      130      129       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dangreen
dangreen merged commit 2891ec4 into main Aug 20, 2026
10 checks passed
@dangreen
dangreen deleted the feat/nanoviews-as-row-transform branch August 20, 2026 16:15
@github-actions github-actions Bot mentioned this pull request Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant