ReviewOS

stacks/stx

stx typecheck does not bind scoped slot props, so any scoped slot fails the check

#2096
Closed glennmichael123 opened this 9 days ago · {{ comments.length }} {{ comments.length === 1 ? 'comment' : 'comments' }}
9 days ago

stx typecheck reports TS2304 Cannot find name '…' for every reference to a scoped slot's props, in every binding form. The runtime binds them correctly - slots.ts handles propsBinding, destructured keys and a single binding - so this is the checker not knowing about a feature the renderer supports.

The effect is that a scoped slot cannot be used at all in a repository that gates on stx typecheck, because using one is guaranteed to fail the step.

Reproduction

<Table :columns="COLUMNS" :data="records()">
  <template #cell="{ row }">
    <p x-text="row.name"></p>
    <span :if="row.isDefault">Default</span>
  </template>
  <template #actions="{ row }">
    <Button :ariaLabel="'Edit ' + row.name" @click="emit('edit', row)">Edit</Button>
  </template>
</Table>
$ bun x --bun stx typecheck CommerceUnitsTable.stx
CommerceUnitsTable.stx:42:76  error TS2304  Cannot find name 'row'.  [template: row.name]
CommerceUnitsTable.stx:43:18  error TS2304  Cannot find name 'row'.  [template: row.isDefault]
CommerceUnitsTable.stx:45:73  error TS2304  Cannot find name 'row'.  [template: row.abbreviation]
CommerceUnitsTable.stx:50:72  error TS2304  Cannot find name 'row'.  [template: 'Edit ' + row.name]
CommerceUnitsTable.stx:50:103  error TS2304  Cannot find name 'row'.  [template: emit('edit', row)]
CommerceUnitsTable.stx:53:74  error TS2304  Cannot find name 'row'.  [template: 'Delete ' + row.name]
CommerceUnitsTable.stx:53:107  error TS2304  Cannot find name 'row'.  [template: emit('delete', row)]
  Checked 1 script block(s) and 11 template expression(s) in 1 file(s) — 7 error(s), 0 warning(s)

Every form is flagged: x-text, :attr, :if, an expression combining the prop with a literal, and passing it to a handler. is flagged too, though that form has a separate runtime problem (#2095).

For contrast, the loop variable in a plain :for in the same file is resolved fine, so the checker does bind template scopes - it just does not treat #name="{ prop }" as introducing one.

Expected

<template #slot="{ a, b }"> and <template #slot="props"> introduce a/b/props into the scope of that template's expressions, the way :for="row in rows()" introduces row.

The slot's prop types would ideally come from the child's <slot :row="row" />, but even binding them as any would be a large improvement over the current state, where correct code cannot pass the check.

Why it matters

This is what blocks a 729-line data-table component in Stacks from being adopted by the 42 dashboard tables that hand-roll <table> instead. The component's cell and actions slots are the right design, the runtime supports them, and stx typecheck is a required CI step - so every migration fails it. stacksjs/stacks#2924 is stuck behind this and #2095.

Verified on 0.2.605 (packages/stx, fa8a407bfa) and on the 0.2.534 build installed in the Stacks checkout.

9 days ago

Fixed on main in d41fefd (fix(typecheck): bind scoped slot parameters lexically), using the shared slot-scope scanner introduced in 370dbf2.

The checker now binds #slot and v-slot:name parameters only within their template: destructured names, aliases, single-name bindings, nested slots, and multiline bindings are covered. This also applies to typed component prop checks and event handlers within a slot. Typos, references outside the slot, and genuine caller-type errors still fail; diagnostic source positions are preserved.

Slot payloads are currently typed as any, as requested for the minimum fix. Inferring their types from the child component is not claimed here.

Regressions: packages/stx/test/typecheck/scoped-slots.test.ts and packages/stx/test/typecheck/stx-virtual-ts.test.ts. Verification: 54 focused tests passed; GitHub CI test, lint/format, and typecheck jobs passed: https://github.com/stacksjs/stx/actions/runs/39436470056 . Commit-artifact publishing is still running.

Version: repository version 0.2.605, with this change on main AFTER v0.2.605. The published 0.2.605 release does not contain it; use the newer commit or the next release. The paired runtime issue #2095 is fixed as well.

Sign in to comment on this issue.