Subscribe on changes!

An type error occurs when building Teleport using the <script setup lang="ts">

avatar
Dec 21st 2020

Version

3.0.4

Reproduction link

https://github.com/Wizard67/rollup-vue

Steps to reproduce

  1. install dependencies.
  2. 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.

avatar
Dec 21st 2020

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.

avatar
Jan 27th 2021

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>
avatar
Oct 30th 2021

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.

avatar
Nov 22nd 2021

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.

avatar
Feb 18th 2022

This bug still occurs when using the latest version of vue.

avatar
Feb 18th 2022

Also indicated by the fact that the issue is still open, yes.

avatar
Mar 30th 2022

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>
avatar
Mar 30th 2022

Suspense has also been added to the VNodeTypes type in LinusBorg's PR.

avatar
Mar 30th 2022

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.

avatar
Jun 9th 2022

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.

avatar
Jun 10th 2022

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

avatar
Jun 13th 2022

...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...

avatar
Jun 24th 2022

...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

avatar
Jul 29th 2022

I've also met with this issue building project for production. Workaround helped.

avatar
Aug 17th 2022

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!