Subscribe on changes!

expose and ts types

avatar
Sep 11th 2022

Vue version

^3.2.39

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-kmnqvn?file=src%2FApp.vue,src%2Fcomponents%2FHelloWorld.vue&terminal=dev

Steps to reproduce

请使用 VSCode 打开

What is expected?

  1. 使用 setup 函数必须 return expose 的方法才能使 ts 的类型检测通过
  2. 如果使用 render 或者 tsx 的方式,无法显视的 return, ts 类型检查就找不到 expose 的属性

What is actually happening?

expose() 和 renturn {}

System Info

System:
    OS: macOS 12.5.1
    CPU: (8) arm64 Apple M1 Pro
    Memory: 5.17 GB / 32.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.16.0 - ~/.nvm/versions/node/v16.16.0/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 8.11.0 - ~/.nvm/versions/node/v16.16.0/bin/npm
  Browsers:
    Chrome: 105.0.5195.102
    Safari: 15.6.1
  npmPackages:
    vue: ^3.2.37 => 3.2.39

Any additional comments?

No response

avatar
Sep 11th 2022

use render property.

render by h fn

<script lang="ts">
import { defineComponent, h } from "vue";

export default defineComponent({
  name: "HelloWorld",
  setup() {
    const msg = () => {
      console.log("msg");
    };
    return { msg };
  },
  render() {
    return h("div", "hello");
  },
});
</script>

render by jsx . add @vitejs/plugin-vue-jsx and set lang is tsx

<script lang="tsx">
import { defineComponent } from "vue";

export default defineComponent({
  name: "HelloWorld",
  setup() {
    const msg = () => {
      console.log("msg");
    };
    return { msg };
  },
  render() {
    return <div>HelloWorld</div>;
  },
});
</script>
avatar
Sep 11th 2022

InstanceType tool would expose many properties that you don't need.

image

or you can create a type to describe Comp instance instead of depend on InstanceType tool.

type CompInst = {
  msg: () => void;
}

in component

// Comp.vue
<script lang="tsx">
import { defineComponent } from "vue";
import { CompInst } from "./type.ts";

export default defineComponent({
  name: "Comp",
  setup() {
    const exposeObj: CompInst = {
      // type hint !
      msg: () => console.log("msg");
    }
    return {
       ...exposeObj
    };
  },
  render() {
    return <div>HelloWorld</div>;
  },
});
</script>

use

<script setup lang="ts">
import { onMounted, ref } from 'vue';
import Comp from './Comp.vue';
import { CompInst } from "./type.ts";

const compRef = ref<CompInst | null>(null);

onMounted(() => {
  // type hint ! and doesn't have any properties that you don't want to expose. 
  compRef.value?.msg();
});
</script>

<template>
  <div>
    <Comp ref="compRef" />
  </div>
</template>
avatar
Sep 11th 2022

thanks.

avatar
Sep 11th 2022

why not this

<script lang="ts">
import { defineComponent, h } from "vue";

export default defineComponent({
  name: "HelloWorld",
  setup(_props, { expose }) {
    const msg = () => {
      console.log("msg");
    };

    expose({ msg })
   
    return () => (
        h("div", "hello");
    )
  });
</script>

don't need return { msg }, use expose can auto mount component Instance.

avatar
Sep 11th 2022

i have a try, and it can't get type hint from ts by InstanceType<typeof HelloWorld>, maybe it is a type bug for vue

image

avatar
Sep 11th 2022

Yes, I also think so, if vue want to mount to the component instance, it must first return { xx }.

avatar
Sep 11th 2022

I used tsx files got the same problem.

// Demo.tsx
import { defineComponent} from "vue";

export default defineComponent({
  name: "HelloWorld",
  setup(_props, { expose }) {
    const msg = () => {
      console.log("msg");
    };

    expose({ msg })
   
    return () => (<div>xxx</div>)
  });
// app.tsx
import { defineComponent, onMounted } from 'vue'
import Demo from './Demo'

export default defineComponent({
  setup (props) {
    const el = ref<InstanceType<typeof Demo>>()

    onMounted(() => {
      console.log(el.value)
      el.value?.msg // Uncaught TypeError: t.value.msg is undefined
    })

    return () => <Demo ref={el}></Demo>
  }
})

https://github.com/vuejs/composition-api/issues/966

Is this the wrong type of vue3?

avatar
May 23rd 2023

same problem

avatar
Sep 2nd 2023

Try vue-macros' exportRender feature with SFC? In that case volar will handle them correctly.

avatar
Jan 4th 2024

same problem + 1,and use render is work for me

avatar
Jan 8th 2024

This is really problematic, just lost 2 hours trying to do something really easy, but using generic components with defineExpose is impossible right now. We need a fix.

#4397 was closed and had no solution.