第 26 题:Vue3 + TS 类型推断(Props 类型推导、Emit 类型推导、Setup 返回值类型)
🎯 一、核心回答(面试官最想听)
Vue3 + TS 的类型推断能力主要依赖 宏(compile-time macro)+ 泛型(generic)+ 类型收窄(narrowing) 。
最常考:
-
defineProps 会自动推断 props 类型
来自编译器(vue-tsc / Volar)在编译时分析宏。
-
defineEmits 会自动推断 emit 的事件名和参数类型。
-
setup 返回值的类型推断 由 TS 自动完成,和 Vue3 的宏无关。
-
Props 类型推断依赖两种写法:
- 基于类型(最常用)
- 基于运行时(runTime props)
🎯 二、Vue3 TS 类型推断深度原理(深入讲,面试加分)
1️⃣ defineProps 编译后变成类型声明(编译时宏)
示例:
typescript
const props = defineProps<{
name: string
age?: number
}>()
编译后(简化):
typescript
// 编译器把 defineProps 的类型直接注入 TS 中
interface Props {
name: string
age?: number
}
const props: Props
🚀 本质:
defineProps 并不是 runtime 代码,而是 compile-time 宏。
编译器会分析宏参数,把类型直接注入到组件类型中。
2️⃣ defineEmits 类型推断机制
typescript
const emit = defineEmits<{
(e: 'change', value: number): void
(e: 'delete', id: string): void
}>()
TS 推断出:
scss
emit('change', 123)
emit('delete', 'abc')
如果写错:
arduino
emit('change', 'xxx') // ❌ 报错,参数类型不对
✨ defineEmits 编译原理:
会生成两个重要东西:
- 事件名联合类型
'change' | 'delete' - 事件函数重载类型推断
这使得 TS 能在 emit 时做严格校验。
3️⃣ 运行时 Props 的自动推导能力
Vue3 宏也能推导 runtime props:
javascript
const props = defineProps({
title: String,
count: Number,
isActive: Boolean
})
TS 会推断为:
typescript
{
title?: string,
count?: number,
isActive?: boolean
}
注意:
所有 runtime 声明的 props 都变成可选,因为父组件可以不传。
4️⃣ 使用 withDefaults 做默认值推断
php
const props = withDefaults(
defineProps<{
username: string
pageSize?: number
}>(),
{
pageSize: 20
}
)
推断结果:
- pageSize 不再是
number | undefined - 自动变成
number
5️⃣ setup 返回值类型推断
Vue 的 setup:
csharp
const count = ref(0)
function add() {}
return { count, add }
推断为:
typescript
{
count: Ref<number>
add: () => void
}
Vue 会自动 unwrap ref 到模板中,但 TS 保留 Ref 类型。
🎯 三、代码示例(完整 TS + Props + Emits)
1)组合 Prop + Emit:
php
<script setup lang="ts">
const props = defineProps<{
value: string
maxLength?: number
}>()
const emit = defineEmits<{
(e: 'update:value', v: string): void
(e: 'reachMax'): void
}>()
function input(v: string) {
if (v.length > (props.maxLength ?? Infinity)) {
emit('reachMax')
}
emit('update:value', v)
}
</script>
全部类型都会自动推断,无需手写 any。
🎯 四、面试官高频追问 & 你该怎么答
❓1:defineProps 为什么不是一个真正的函数?为什么只能在
高分回答:
因为 defineProps 是 编译器宏,不是运行时函数,它只在编译期生效。
Vue 编译器直接将 defineProps 的类型写入组件类型系统。
❓2:defineProps 的类型参数和运行时参数同时写,会怎样?
例子:
css
defineProps<{
name: string
}>({
name: Number
})
结果:类型冲突,TS 会报错,因为宏不允许有两个来源。
❓3:和 Vue2 的 props + emit 相比,Vue3 的 TS 强在哪里?
高频 + 加分回答:
Vue3 做到了:
- 事件名称的自动补全
- 参数类型自动检测
- props 类型自动推导
- default 值变窄(withDefaults)
- emit 可写多重 overload(相当专业)
Vue2 用 TS 非常吃力,Vue3 解决了痛点。
❓4:有类型推断的 defineModel(v-model 语法糖)是什么?
Vue3.4 新增:
c
const model = defineModel<string>()
自动推断:
- modelValue 类型
- update:modelValue 参数类型
这是 Vue TS 体验最强的一部分。
🎯 五、高分总结(面试直接背)
Vue3 的 defineProps / defineEmits / defineModel 都是编译期宏,不存在于运行时代码中。
它们通过 TS 泛型 + 编译器静态分析,实现 Props、Emit、v-model 的完整类型推断,是 Vue3 与 TS 最强结合点。
运行时 props 写法也可以推断类型,但不如泛型写法强。
setup 返回值由 TS 自动推断,与宏无关。