Skip to content

fix(column): add phantom of prop for typed slot inference#8485

Open
YevheniiKotyrlo wants to merge 1 commit intoprimefaces:masterfrom
YevheniiKotyrlo:fix/column-typed-slots
Open

fix(column): add phantom of prop for typed slot inference#8485
YevheniiKotyrlo wants to merge 1 commit intoprimefaces:masterfrom
YevheniiKotyrlo:fix/column-typed-slots

Conversation

@YevheniiKotyrlo
Copy link

@YevheniiKotyrlo YevheniiKotyrlo commented Mar 11, 2026

Defect Fixes

Fixes #8483

Column's #body, #editor, #node, and #loading slots are all typed as any. Even with DataTable's T generic (#8444), Column is a separate component — Vue's type system cannot propagate parent component generics to child component slots.

This affects the most common DataTable interaction: accessing row data in Column templates.

Related: #7426, #6041

Reproducer: StackBlitzbun run type-check (0 errors, typo undetected) → bun run patch && bun run type-check (TS2339 catches it)

Problem

<DataTable :value="orders">  <!-- T = Order (inferred from #8444) -->
  <Column field="id">
    <template #body="{ data }">
      {{ data.stauts }}
      <!-- No error — `data` is `any` despite DataTable knowing T = Order -->
    </template>
  </Column>
</DataTable>

DataTable's T (from #8444) types DataTable's own slots (#groupheader, #expansion), but cannot flow to Column — Vue provides no mechanism for parent generics to propagate to child component slots.

Solution: phantom of prop

An opt-in of prop carries type information without affecting runtime:

<DataTable :value="orders">
  <Column :of="orders" field="id">
    <template #body="{ data }">
      {{ data.stauts }}
      <!-- TS2339: Property 'stauts' does not exist on type 'Order' -->
    </template>
  </Column>
</DataTable>

By binding :of="orders" (the same array passed to DataTable's :value), TypeScript infers T from the array type and flows it to all slot scoped data. The of prop is never read at runtime — it exists purely for TypeScript inference.

Without :of, T defaults to any — fully backward compatible. Adoption is opt-in per Column.

Why of?

  • Short, readable: <Column :of="orders" field="name">
  • Semantically clear: "this column is of this data"
  • No conflict with existing Column props
  • Works with both DataTable and TreeTable

Changes

  • ColumnSlots<T = any>data: T, node: T in body/node/editor/loading slots, field: (item: T) => string
  • Generic constructor with phantom prop: new <T = any>(props: ColumnProps & { of?: readonly T[] | T[] | null })
  • readonly T[] supports read-only query results (e.g., Zero, TanStack Query)

What gets typed

Slot Without :of With :of="orders"
#body data / node any T
#node data / node any T
#editor data any T
#loading data any T
#body field callback (item: any) => string (item: T) => string

Scope / Impact

  • No breaking changes — without :of, T defaults to any (same as before)
  • No runtime changesof is never read by Column's runtime code
  • Opt-in — consumers add :of="dataArray" only where they want typed slots
  • Works with DataTable and TreeTable

Verification

Gate Result
vue-tsc --noEmit with strictTemplates 0 new errors
Column without :of (backward compat) data: any, zero errors
Column with :of="orders" Full T inference, autocomplete works
All CI gates (format, lint, test, build) PASS

Series

This is part of a series bringing generic type inference to all PrimeVue data components:

PR Components Status
#8444 DataTable, DatePicker Open
#8484 Select, MultiSelect, Listbox Open
#8485 Column (phantom of prop) This PR
#8489 AutoComplete, CascadeSelect, DataView Open
#8490 Carousel, Galleria, Timeline Open
#8491 OrderList, PickList Open
#8493 VirtualScroller, SelectButton, InputChips Open

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.

Column: #body/#editor/#node slot data is any, even with typed DataTable

1 participant