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

expose and ts types #6643

Closed
WalkAlone0325 opened this issue Sep 11, 2022 · 13 comments
Closed

expose and ts types #6643

WalkAlone0325 opened this issue Sep 11, 2022 · 13 comments

Comments

@WalkAlone0325
Copy link
Contributor

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

@Dedicatus546
Copy link

Dedicatus546 commented Sep 11, 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>

@Dedicatus546
Copy link

Dedicatus546 commented Sep 11, 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>

@WalkAlone0325
Copy link
Contributor Author

thanks.

@WalkAlone0325
Copy link
Contributor Author

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.

@Dedicatus546
Copy link

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

@WalkAlone0325
Copy link
Contributor Author

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

@mengdu
Copy link

mengdu commented Sep 11, 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>
  }
})

vuejs/composition-api#966

Is this the wrong type of vue3?

@sadeghbarati
Copy link

sadeghbarati commented Oct 15, 2022

@sxzz sxzz added scope: types 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Mar 29, 2023
@Hccake
Copy link

Hccake commented May 23, 2023

same problem

@so1ve
Copy link
Member

so1ve commented Sep 2, 2023

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

@trcat
Copy link

trcat commented Jan 4, 2024

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

@NathanAP
Copy link

NathanAP commented Jan 8, 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.

@yyx990803 yyx990803 removed the 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. label Jul 17, 2024
@yyx990803
Copy link
Member

yyx990803 commented Jul 17, 2024

This is not considered a bug, because there is no way in TypeScript for the expose() call to affect the return type of the parent defineComponent() call. It's not just possible.

With the latest version of vue-tsc, this is working correctly when using defineExpose() inside <script setup>. If you for some reason has to continue using defineComponent(), then you will have to declare the export the exposed type as an interface:

// use this in other components
export interface Exposed {}

defineComponent({
  setup(_, { expose }) {
    expose({ /* ... */ } satisfies Exposed)
  }
})

@github-actions github-actions bot locked and limited conversation to collaborators Aug 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants