Skip to content
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

Circular reference caused by <Comp v-bind="{ ...$attrs }"> as single root el #5197

Closed
darthf1 opened this issue Feb 18, 2025 · 5 comments · Fixed by #5135
Closed

Circular reference caused by <Comp v-bind="{ ...$attrs }"> as single root el #5197

darthf1 opened this issue Feb 18, 2025 · 5 comments · Fixed by #5135
Labels
good reproduction ✨ This issue provides a good reproduction, we will be able to investigate it first 🔩 p2-edge-case

Comments

@darthf1
Copy link

darthf1 commented Feb 18, 2025

Vue - Official extension or vue-tsc version

2.2.2

VSCode version

n/a

Vue version

3.5.13

TypeScript version

5.7.3

System Info

n/a

package.json dependencies

{
  "devDependencies": {
    "vue-component-type-helpers": "2.2.2",
    "vue-tsc": "2.2.2"
  }
}

Steps to reproduce

For a long time I'm using wrapper components in my Vue project. On vue-tsc 2.1.0, I got no errors and my Vue application can build. With 2.2.2, I got a few ts errors (because of better type inference in the new version), but it is not possible to make both Vue and vue-tsc happy.

1. My starting point: vue-tsc 2.1.10

2. Simple upgrade to vue-tsc 2.2.2

src/components/MyButton.vue:8:3 - error TS7022: '__VLS_2' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

  8   <b-button v-bind="{ ...$props, ...$attrs }">
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  9     <template v-for="(_, name) in $slots" #[name]="scope">
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 11     </template>
    ~~~~~~~~~~~~~~~
 12   </b-button>
    ~~~~~~~~~~~~~

src/components/MyButton.vue:9:26 - error TS7022: 'name' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

9     <template v-for="(_, name) in $slots" #[name]="scope">
                           ~~~~

Found 2 errors in the same file, starting at: src/components/MyButton.vue:8

3. Small rewrite to specifically define slot and emit types, via InstanceType

[@vue/compiler-sfc] Unresolvable type reference or unsupported built-in utility type

/home/projects/gqairbqnbq.github/src/components/MyButton.vue
4  |  defineProps<BButtonProps>();
5  |  defineSlots<InstanceType<typeof BButton>['$slots']>();
6  |  defineEmits<InstanceType<typeof BButton>['$emit']>();
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7  |  </script>
8  |

4. Same as previous, but now using vue-component-type-helpers

[plugin:vite:vue] [@vue/compiler-sfc] Unresolvable type: TSConditionalType

/home/projects/github-g8ym7afd-7czwcytj/node_modules/vue-component-type-helpers/index.d.ts
10 |      emit: any;
11 |  }, ...args: any) => any ? NonNullable<S> : {};
12 |  export type ComponentEmit<T> = T extends new (...args: any) => {
   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13 |      $emit: infer E;
   |  ^^^^^^^^^^^^^^^^^^^
14 |  } ? NonNullable<E> : {};
   |  ^^^^^^^^^^^^^^^^^^^^^^^

5. Same as previous, but now using complex types

What is expected?

I was hoping for either 3 or 4 to work. Both 3 and 4 work great for type inference in my IDE (phpstorm), but I cannot get my app to build.

What is actually happening?

TS is happy, Vue is not.

Link to minimal reproduction

No response

Any additional comments?

No response

@KazariEX KazariEX added duplicate This issue or pull request already exists and removed pending triage labels Feb 18, 2025
@darthf1
Copy link
Author

darthf1 commented Feb 18, 2025

Hi @KazariEX, it took me quite some time to create this issue so I was quite bummed to see it just closed as duplicate. I'm trying to go from #5116 to an example that works, but I'm unable to.

Reading:

https://stackblitz.com/edit/github-g8ym7afd-khku9qew?file=src%2Fcomponents%2FMyButton.vue

I tried a new version in the repo above, is that what the solution is? Does all the slot/event/props inference still work?

@KazariEX
Copy link
Collaborator

KazariEX commented Feb 18, 2025

Sorry I'm too tired today, part of this issue is duplicate, while the other part can be simplified as:

<template>
  <Comp v-bind="{ ...$props, ...$attrs }" />
</template>

About compiler / runtime issue please report at vuejs/core repo.

@KazariEX KazariEX added 🔩 p2-edge-case and removed duplicate This issue or pull request already exists labels Feb 18, 2025
@KazariEX KazariEX reopened this Feb 18, 2025
@KazariEX KazariEX added the good reproduction ✨ This issue provides a good reproduction, we will be able to investigate it first label Feb 18, 2025
@KazariEX
Copy link
Collaborator

KazariEX commented Feb 18, 2025

A root level comment should resolve the problem:

<template>
  <!-- skip root el resolution -->
  <Comp v-bind="{ ...$props, ...$attrs }" />
</template>

@KazariEX
Copy link
Collaborator

This is also valid:

<template>
  <Comp v-bind="$attrs" :="$props" />
</template>

Because v-bind="$attrs" will be specially processed to avoid circular references.

@KazariEX KazariEX changed the title Cannot satisfy both Vue and vue-tsc with wrapper component Circular reference caused by <Comp v-bind="{ ...$attrs }"> as single root el Feb 19, 2025
@darthf1
Copy link
Author

darthf1 commented Feb 20, 2025

This is also valid:

<template>
  <Comp v-bind="$attrs" :="$props" />
</template>

Because v-bind="$attrs" will be specially processed to avoid circular references.

Thanks! Was able to resolve my issues for now with this solution, and using a simple defineSlots<Record<string, any>>()

Much appreciated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good reproduction ✨ This issue provides a good reproduction, we will be able to investigate it first 🔩 p2-edge-case
Projects
None yet
2 participants