Skip to content

Rendering generic slots fails with Argument of type '...' is not assignable to parameter of type 'NonNullable<...> #5316

@PierreSchwang

Description

@PierreSchwang

Vue - Official extension or vue-tsc version

2.2.8

VSCode version

not using VSC

Vue version

3.5.13

TypeScript version

5.8.3

System Info

System:
    OS: Windows 11 10.0.26100
    CPU: (16) x64 AMD Ryzen 7 5700G with Radeon Graphics
    Memory: 10.47 GB / 27.90 GB
  Binaries:
    Node: 23.5.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 4.5.1 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 9.7.2 - C:\Program Files\nodejs\npm.CMD
    pnpm: 8.3.1 - ~\AppData\Local\pnpm\pnpm.EXE
  Browsers:
    Edge: Chromium (131.0.2903.86)
    Internet Explorer: 11.0.26100.1882

package.json dependencies

{
  "dependencies": {
    "vue": "3.5.13"
  },
  "devDependencies": {
    "@tsconfig/node22": "22.0.1",
    "@types/node": "22.14.0",
    "@vitejs/plugin-vue": "5.2.3",
    "@vue/tsconfig": "0.7.0",
    "npm-run-all2": "7.0.2",
    "typescript": "5.8.3",
    "vite": "6.2.4",
    "vite-plugin-vue-devtools": "7.7.2",
    "vue-tsc": "2.2.8"
  }
}

Steps to reproduce

I've created a simple Vue component with a generic set: T extends ({id: string} & Record<string, unknown>)
The actual type is passed implicitly by a prop:

const props = defineProps({
  data: {
    type: Object as PropType<T[]>,
    required: true
  },
  columns: {
    type: Array as PropType<(keyof T & string)[]>,
    required: true
  }
})

Based on the generic, defineSlots is used to define slots:

defineSlots<{
  [K in keyof T as `header-${K & string}`]: () => unknown
} & {
  [K in keyof T as `cell-${K & string}`]: (props: { value: T[K], item: T }) => unknown
} & {
  "header-__action": () => unknown,
  "cell-__action": (props: { item: T }) => unknown
}>()

And used for the actual rendering:

<th v-for="column in props.columns" :key="column">
  <slot v-if="$slots[`header-${column}`]" :name="`header-${column}`"></slot>
  <template v-else>{{ column }}</template>
</th>

<th v-if="$slots['cell-__action']">
  <slot v-if="$slots['header-__action']" name="header-__action"></slot>
  <template v-else>Actions</template>
</th>

<slot v-if="$slots[`cell-${column}`]" :name="`cell-${column}`" :value="row[column]" :item="row"></slot>

<slot name="cell-__action" :item="row as T"></slot>

(see the referenced link to the minimal reproduction)

Finally used in my page to render data:

const data = [
  {
    id: 'first',
    name: 'John Doe',
    married: false
  },
  // ....
]
<GenericTable :columns="['name', 'married']" :data="data">
  <template #header-married>Is married?</template>
  <template #cell-married="{value}">{{ value ? 'Yes' : 'No' }}</template>
  <template #cell-__action>
    <a>Show more info</a>
  </template>
</GenericTable>

The rendering works without any issues:
Image

But the type-check fails.

What is expected?

The type-check should not fail at the slot calls (with dynamic and static names)

What is actually happening?

The type-check fails at every single slot in the template

src/components/GenericTable.vue:31:10 - error TS2345: Argument of type '{}' is not assignable to parameter of type 'NonNullable<(Readonly<{ [K in keyof T as `header-${K & string}`]: () => unknown; } & { [K in keyof T as `cell-${K & string}`]: (props: { value: T[K]; item: T; }) => unknown; } & { "header-__action": () => unknown; "cell-__action": (props: { item: T; }) => unknown; }> & { [K in keyof T as `header-${K & string}`]: () ...'.

31         <slot v-if="$slots[`header-${column}`]" :name="`header-${column}`"></slot>
            ~~~~

src/components/GenericTable.vue:35:10 - error TS2345: Argument of type '{}' is not assignable to parameter of type 'NonNullable<({ [K in keyof T as `header-${K & string}`]: () => unknown; } & { [K in ke
yof T as `cell-${K & string}`]: (props: { value: T[K]; item: T; }) => unknown; } & { "header-__action": () => unknown; "cell-__action": (props: { item: T; }) => unknown; })["header-__action"] & (() => unknown)> extends (props: infe...'.

35         <slot v-if="$slots['header-__action']" name="header-__action"></slot>
            ~~~~

src/components/GenericTable.vue:43:10 - error TS2345: Argument of type '{ value: T[keyof T & string]; item: T; }' is not assignable to parameter of type 'NonNullable<(Readonly<{ [K in keyof T as `header
-${K & string}`]: () => unknown; } & { [K in keyof T as `cell-${K & string}`]: (props: { value: T[K]; item: T; }) => unknown; } & { "header-__action": () => unknown; "cell-__action": (props: { item: T; }) => unknown; }> & { [K in keyof T as `header-${K & string}`]: () ...'.

43         <slot v-if="$slots[`cell-${column}`]" :name="`cell-${column}`" :value="row[column]"
            ~~~~

src/components/GenericTable.vue:50:10 - error TS2345: Argument of type '{ item: T; }' is not assignable to parameter of type 'NonNullable<({ [K in keyof T as `header-${K & string}`]: () => unknown; } & { [K in keyof T as `cell-${K & string}`]: (props: { value: T[K]; item: T; }) => unknown; } & { "header-__action": () => unknown; "cell-__action": (props: { item: T; }) => unknown; })["cell-__action"] & ((props: { ...; }) => unknown)> extends...'.

50         <slot name="cell-__action" :item="row as T"></slot>
            ~~~~


Found 4 errors.

ERROR: "type-check" exited with 2.

Link to minimal reproduction

https://github.com/PierreSchwang/vue-generic-slots-reproduction

Any additional comments?

It previously worked for me and just occurred after updating vue-tsc. I could pinpoint it breaking when updating from 2.2.2 to 2.2.4.
Not even sure if the issue is even fitting into this repository or if it is because of an updated (peer-)dependency.

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateThis issue or pull request already exists

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions