An type error occurs when building Teleport using the <script setup lang="ts">
Version
3.0.4
Reproduction link
https://github.com/Wizard67/rollup-vue
Steps to reproduce
- install dependencies.
- run build script.
What is expected?
Building Success.
What is actually happening?
typescript throws the following error.
semantic error TS2345: Argument of type '{ new (): { $props: VNodeProps & TeleportProps; }; __isTeleport: true; }' is not assignable to parameter of type 'string | ComponentOptions<any, any, any, Record<string, ComputedGetter<any> | WritableComputedOptions<any>>, MethodOptions, any, any, any> | ... 9 more ... | ClassComponent'.
Type '{ new (): { $props: VNodeProps & TeleportProps; }; __isTeleport: true; }' is not assignable to type 'ComponentPublicInstanceConstructor<any, any, any, any, Record<string, ComputedGetter<any> | WritableComputedOptions<any>>, MethodOptions>'.
Types of property '__isTeleport' are incompatible.
Type 'true' is not assignable to type 'never'.
It looks like the incompatibility between the createBlock and Teleport types is the cause of the.
createBlock()
accepts the following types as its first argument:
export type VNodeTypes =
| string
| VNode
| Component
| typeof Text
| typeof Static
| typeof Comment
| typeof Fragment
| typeof TeleportImpl
| typeof SuspenseImpl
Those *Impl
types ares used internally only though. Externally, these implementations are cast to soemthing different - typeof Teleport != TeleportImpl
.
First instinct is that we should use Teleport
and Suspense
here as well, instead of their internal *Impl
counterparts.
This isn't a problem when not using script setup
as the created render function never runs through the typescript compiler in these cases. In other words: a normal component creates the same render function, and at runtime it works - but only with script setup
this is actually catched by the TS compiler.
Workaround:
<script lang="ts" setup>
import {
Teleport as teleport_,
TeleportProps,
VNodeProps
} from 'vue'
const Teleport = teleport_ as {
new (): {
$props: VNodeProps & TeleportProps
}
}
</script>
<template>
<component :is="Teleport">
</component>
</template>
Hitting this or a very similar issue in my vue-loader/webpack-based project even without <script setup>
/ with an explicit defineComponent
call in my <script lang="ts">
section. The issue just started after a recent update to vue-loader (which now apparently sends template code through the TypeScript compiler).
Thanks for sharing the workaround, it seems to work in my case as well.
Same problem with Vue 3.2.22 SFC + <script lang="ts">
+ defineComponent
in a webpack project:
TS2345: Argument of type '{ new (): { $props: VNodeProps & TeleportProps; }; __isTeleport: true; }' is not assignable to parmeter of type 'VNodeTypes | ClassComponent'.
Type '{ new (): { $props: VNodeProps & TeleportProps; }; __isTeleport: true; }' is not assignable to type 'ComponentPublicInstaneConstructor<any, any, any, any, ComputedOptions, MethodOptions>'.
Types of property '__isTeleport' are incompatible.
Type 'true' is not assignable to type 'undefined'.
Workaround works, thanks.
We also had to do the same for Suspense.
<template>
<component :is="Suspense">
</component>
</template>
<script lang="ts">
import {Suspense as suspense_, VNodeProps, SuspenseProps} from 'vue';
export default defineComponent({
name: 'App',
setup() {
const Suspense = suspense_ as {
new (): {
$props: VNodeProps & SuspenseProps
}
}
return {
Suspense,
}
},
});
</script>
I was adding the workaround for people that have the issue with Suspense as the workaround was not in the thread and there doesn't seem to be a separate issue for that.
I just came across this when doing watch with webpack. Maybe it helps somebody or is related to this.
I get the error when I have my <template>
like this:
<template>
<div>
<Teleport to="body">
<transition name="fade">
<div>...</div>
</transition>
</Teleport>
</div>
</template>
When I remove the leading <div>
, the Error disappears
<template>
<Teleport to="body">
<transition name="fade">
<div>...</div>
</transition>
</Teleport>
</template>
But it's not consistent... It seems the error only appears the first time, then after a change it seems to disappear.
If I add or remove the leading div while watch is running, the type error always disappears. If I have no leading div, the error appears on the second watch run, not the first.
Additionaly, if I remove the leading div, I get a Vue Warning: "Extraneous non-props attributes (name) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. " (which probably makes sense, but still, maybe it helps)
Feel free to delete this comment if it is off topic.
I just came across this when doing watch with webpack. Maybe it helps somebody or is related to this.
I get the error when I have my
<template>
like this:<template> <div> <Teleport to="body"> <transition name="fade"> <div>...</div> </transition> </Teleport> </div> </template>
When I remove the leading
<div>
, the Error disappears<template> <Teleport to="body"> <transition name="fade"> <div>...</div> </transition> </Teleport> </template>
But it's not consistent... It seems the error only appears the first time, then after a change it seems to disappear.
If I add or remove the leading div while watch is running, the type error always disappears. If I have no leading div, the error appears on the second watch run, not the first.
Additionaly, if I remove the leading div, I get a Vue Warning: "Extraneous non-props attributes (name) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. " (which probably makes sense, but still, maybe it helps)
Feel free to delete this comment if it is off topic.
I am having the same issue - but sadly your solution doesn't fix it for me - I am running in a Laravel Mix stack (which I converted to Typescript) and I am only seeing this error when running a production build - dev builds don't throw the error
...I am only seeing this error when running a production build - dev builds don't throw the error
for me it's the opposite 🤷♂️ runs fine on prod build, only watch and dev complain...
...I am only seeing this error when running a production build - dev builds don't throw the error
for me it's the opposite 🤷♂️ runs fine on prod build, only watch and dev complain...
I also get it only in the production build 🤷♂️ weird
Additionaly, if I remove the leading div, I get a Vue Warning: "Extraneous non-props attributes (name) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. " (which probably makes sense, but still, maybe it helps)
I can't speak to the dev/prod build issue, but with regard to the attribute inheritance, you can solve that issue by explicitly binding the Teleport props to the dynamic component. I'm currently using the following as a generalized Teleport replacement (thanks @LinusBorg for the head start!):
<!-- VueTeleport.vue -->
<template>
<component :is="Teleport" v-bind="props">
<slot></slot>
</component>
</template>
<script setup lang="ts">
import { Teleport as teleport_, type TeleportProps, type VNodeProps } from 'vue';
const Teleport = teleport_ as {
new (): {
$props: VNodeProps & TeleportProps
}
};
const props = defineProps<{
to: TeleportProps['to'],
disabled?: TeleportProps['disabled'],
}>();
</script>
Note that if the Teleport API changes then any changes would also need to be propagated to the props
definition here. Hopefully this issue will be solved before that even becomes an problem, though, lol. Also, FWIW, I haven't had a need for non-prop attributes to be further inherited (via v-bind="$attrs"
on the <slot>
or a wrapper <div>
, for instance), since they'd likely just be passed in directly with the slotted content. I can't say how the built-in Teleport component behaves in this regard as I've completely replaced my usage of it with this component. Hope this helps someone!