父组件准确获取子组件实例类型需子组件用defineExpose显式暴露接口+父组件用泛型ref标注类型;否则TS默认推导为any或ComponentPublicInstance,无法识别自定义属性和方法。

在 Vue 3 中,父组件要**准确获取子组件实例类型**(即获得 TypeScript 类型提示、避免 `any`),关键不是只靠 `` 语法,而是必须配合 `defineExpose` 显式暴露接口 + 正确的类型标注。单纯写 `ref="xxx"` 只能拿到运行时实例,TypeScript 默认推导为 `any` 或 `ComponentPublicInstance`,无法识别自定义方法或数据。
1. 子组件必须用 defineExpose 明确导出结构
子组件内部不能只依赖默认暴露,必须主动声明哪些属性/方法对外可见:
- 不写 defineExpose:父组件即使拿到实例,也无法访问子组件内部的
ref、computed或自定义方法,TS 也无类型信息; - 写了但暴露空对象(
defineExpose({})):实例可获取,但类型为空,仍无意义; - 正确做法是暴露一个具名接口或内联类型,例如:
<script setup lang="ts"></script>
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
const reset = () => { count.value = 0 }
defineExpose({ count, double, reset }) // ← 关键:显式暴露
2. 父组件使用泛型 ref + 类型断言(推荐)
在父组件中,不要仅依赖 ref=null 的默认类型,而应显式标注子组件实例类型:
- 先为子组件创建一个类型定义(可复用):
export interface ChildExposed {
count: Ref
double: ComputedRef
reset: () => void;
}
- 然后在父组件中绑定 ref 并标注类型:
<script setup lang="ts"></script>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
import type { ChildExposed } from './types'
const childRef = ref
// 或更简洁写法(Vue 官方推荐):
// const childRef = ref
const callReset = () => {
if (childRef.value) childRef.value.reset() // ← TS 精准提示 reset 方法
}
onMounted(() => {
console.log(childRef.value?.double) // ← double 是 ComputedRef
})
3. 避免常见误区
-
不要依赖 $refs.childRef 的隐式类型:它默认是
ComponentPublicInstance,没有你暴露的字段,TS 不会自动补全; -
不要省略 ref 初始化为 null:`ref
()` 会报错,必须是 `ref (null)`; - 不要在未挂载时访问 childRef.value:`onMounted` 或 `v-if` 控制显示后再调用,否则值为 null;
- 子组件未使用 defineExpose 时,即使加了类型也没用:暴露和消费必须配套。











