Skip to content

fix(language-core): prevent eager inference of slot props from generics #5247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 3, 2025

Conversation

KazariEX
Copy link
Member

@KazariEX KazariEX commented Mar 3, 2025

fix #5228, fix #5246

Copy link

pkg-pr-new bot commented Mar 3, 2025

Open in Stackblitz

vue-component-meta

npm i https://pkg.pr.new/vuejs/language-tools/vue-component-meta@5247

vue-component-type-helpers

npm i https://pkg.pr.new/vuejs/language-tools/vue-component-type-helpers@5247

@vue/language-plugin-pug

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-plugin-pug@5247

@vue/language-server

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-server@5247

@vue/language-service

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-service@5247

@vue/language-core

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-core@5247

vue-tsc

npm i https://pkg.pr.new/vuejs/language-tools/vue-tsc@5247

@vue/typescript-plugin

npm i https://pkg.pr.new/vuejs/language-tools/@vue/typescript-plugin@5247

commit: a6f03a3

@KazariEX KazariEX merged commit 860ffca into vuejs:master Mar 3, 2025
6 checks passed
@KazariEX KazariEX deleted the fix/issue-5246 branch March 3, 2025 20:11
@targetlucked69
Copy link

is this fix available in 2.2.8 already? thanks!

@KazariEX
Copy link
Member Author

@targetlucked69 It was included in the v3.0.0-alpha.0.

@shossk
Copy link

shossk commented Mar 31, 2025

@KazariEX When will v3.0.0 be released? I'm having trouble.

@KazariEX
Copy link
Member Author

@shossk see #5261.

@sandros94
Copy link

@KazariEX sorry for the ping, I've been following this for quite a bit hoping to find a fix myself but I'm getting more and more lost.

Over Nuxt UI I'm trying to fix a type utility to dynamically type slot names. The developer passes an array to the component, where each item may have its own slot name and depending on the component it might also have one or multiple suffix to that slot name (eg: <name>, <name>-leading, <name>-trailing). In runtime this named slot will only be bound to that particular item in the input array, so it is only a matter of typing everything correctly.

I tried to mimic as much as possible the implementation from this PR, but I'm still getting error:

../src/runtime/components/Test.vue:40:8 - error TS2345: Argument of type '{ item: T; index: number; }' is not assignable to parameter of type 'NonNullable<(Readonly<{ item: (props: { item: T; index: number; }) => any; 'item-trailing': (props: { item: T; index: number; }) => any; } & DynamicSlots<T, "trailing", { index: number; }>> & { ...; } & DynamicSlots<...>)["item" | ... 2 more ... | `${T["slot"] & string}-trailing`]> extends (props: infer P) => any ? P : {}'.

../src/runtime/components/Test.vue:45:8 - error TS2345: Argument of type '{ item: T; index: number; }' is not assignable to parameter of type 'NonNullable<(Readonly<{ item: (props: { item: T; index: number; }) => any; 'item-trailing': (props: { item: T; index: number; }) => any; } & DynamicSlots<T, "trailing", { index: number; }>> & { ...; } & DynamicSlots<...>)["item" | ... 2 more ... | `${T["slot"] & string}-trailing`]> extends (props: infer P) => any ? P : {}'.

The following is the smallest reproduction I was able to make (I'm interested in the DynamicSlots utility type):

<script lang="ts">
interface TestItem {
  label: string
  slot?: string
}

interface TestProps<T extends TestItem> {
  items: T[]
}

type DynamicSlots<
  T extends { slot?: string },
  S extends string | undefined = undefined,
  D extends object = {}
> = {
  [K in (T['slot'] & string | `${T['slot'] & string}-${S & string}`)]: (props: {
    item: Extract<T, { slot: S extends undefined
      ? K
      : K extends `${infer Base}-${S & string}`
        ? Base
        : K }>
  } & D) => any
}

type TestSlots<T extends TestItem> = {
  'item': (props: { item: T, index: number }) => any
  'item-trailing': (props: { item: T, index: number }) => any
} & DynamicSlots<T, 'trailing', { index: number }>

</script>

<script setup lang="ts" generic="T extends TestItem">
defineProps<TestProps<T>>()
defineSlots<TestSlots<T>>()
</script>

<template>
  <div v-for="(item, index) of $props.items" :key="index">
    <span>
      <slot
        :name="((item.slot ? item.slot : 'item') as keyof TestSlots<T>)"
        :item="item"
        :index="index"
      />
      <slot
        :name="((item.slot ? `${item.slot}-trailing` : 'item-trailing') as keyof TestSlots<T>)"
        :item="item"
        :index="index"
      />
    </span>
  </div>
</template>

@KazariEX
Copy link
Member Author

@sandros94 How did you solve this problem?

@sandros94
Copy link

@sandros94 How did you solve this problem?

I'm not sure I have resolved it tbh, but I realized that I was getting those typecheck errors only in the local docs and not playgrounds, so there might be an issue on our end.

And considering it is the first time I debug vue-tsc I tend to think about me doing something wrong. If you have some free time I would gladly take any advice and much appreciated if you could take a look at that example I've made 🙏

@KazariEX
Copy link
Member Author

KazariEX commented Apr 12, 2025

This is the final code that caused the problem:

image

You can install the Volar Labs extension and check the virtual code of this sfc.

Even without going through our normalize function, this slot still cannot be directly called:

image

@sandros94
Copy link

This is the final code that caused the problem:

[...]

You can install the Volar Labs extension and check the virtual code of this sfc.

you mean this extension?

Could I ask a link to docs or a bit of guidance on how to correctly debug it and access its virtual code?

Even without going through our normalize function, this slot still cannot be directly called:

You mean that it is not possible to pick that specific slot? Sorry, I might be missing the meaning of some terminology like "calling slots"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants