core
core copied to clipboard
expose and ts types
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?
- 使用
setup
函数必须return
expose
的方法才能使 ts 的类型检测通过 - 如果使用
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
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>
InstanceType
tool would expose many properties that you don't need.
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>
thanks.
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.
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
Yes, I also think so, if vue
want to mount to the component instance, it must first return { xx }
.
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?
Check these
https://github.com/vuejs/core/issues/4397
https://github.com/logaretm/vee-validate/blob/main/packages/vee-validate/src/Form.ts#L229C1-L248C3
same problem
Try vue-macros' exportRender
feature with SFC? In that case volar will handle them correctly.
same problem + 1,and use render
is work for me
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.
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)
}
})