第 27 题:Vue3 + TS 类型推断(Props 类型推导、Emit 类型推导、Setup 返回值类型)

118 阅读3分钟

第 26 题:Vue3 + TS 类型推断(Props 类型推导、Emit 类型推导、Setup 返回值类型)


🎯 一、核心回答(面试官最想听)

Vue3 + TS 的类型推断能力主要依赖 宏(compile-time macro)+ 泛型(generic)+ 类型收窄(narrowing)

最常考:

  1. defineProps 会自动推断 props 类型
    来自编译器(vue-tsc / Volar)在编译时分析宏。

  2. defineEmits 会自动推断 emit 的事件名和参数类型。

  3. setup 返回值的类型推断 由 TS 自动完成,和 Vue3 的宏无关。

  4. Props 类型推断依赖两种写法:

    • 基于类型(最常用)
    • 基于运行时(runTime props)

🎯 二、Vue3 TS 类型推断深度原理(深入讲,面试加分)


1️⃣ defineProps 编译后变成类型声明(编译时宏)

示例:

const props = defineProps<{
  name: string
  age?: number
}>()

编译后(简化):

// 编译器把 defineProps 的类型直接注入 TS 中
interface Props {
  name: string
  age?: number
}
const props: Props

🚀 本质:

defineProps 并不是 runtime 代码,而是 compile-time 宏。
编译器会分析宏参数,把类型直接注入到组件类型中。


2️⃣ defineEmits 类型推断机制

const emit = defineEmits<{
  (e: 'change', value: number): void
  (e: 'delete', id: string): void
}>()

TS 推断出:

emit('change', 123)
emit('delete', 'abc')

如果写错:

emit('change', 'xxx') // ❌ 报错,参数类型不对

✨ defineEmits 编译原理:

会生成两个重要东西:

  1. 事件名联合类型 'change' | 'delete'
  2. 事件函数重载类型推断

这使得 TS 能在 emit 时做严格校验。


3️⃣ 运行时 Props 的自动推导能力

Vue3 宏也能推导 runtime props:

const props = defineProps({
  title: String,
  count: Number,
  isActive: Boolean
})

TS 会推断为:

{
  title?: string,
  count?: number,
  isActive?: boolean
}

注意:
所有 runtime 声明的 props 都变成可选,因为父组件可以不传。


4️⃣ 使用 withDefaults 做默认值推断

const props = withDefaults(
  defineProps<{
    username: string
    pageSize?: number
  }>(),
  {
    pageSize: 20
  }
)

推断结果:

  • pageSize 不再是 number | undefined
  • 自动变成 number

5️⃣ setup 返回值类型推断

Vue 的 setup:

const count = ref(0)

function add() {}

return { count, add }

推断为:

{
  count: Ref<number>
  add: () => void
}

Vue 会自动 unwrap ref 到模板中,但 TS 保留 Ref 类型。


🎯 三、代码示例(完整 TS + Props + Emits)


1)组合 Prop + Emit:

<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 的类型参数和运行时参数同时写,会怎样?

例子:

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 新增:

const model = defineModel<string>()

自动推断:

  • modelValue 类型
  • update:modelValue 参数类型

这是 Vue TS 体验最强的一部分。


🎯 五、高分总结(面试直接背)

Vue3 的 defineProps / defineEmits / defineModel 都是编译期宏,不存在于运行时代码中。
它们通过 TS 泛型 + 编译器静态分析,实现 Props、Emit、v-model 的完整类型推断,是 Vue3 与 TS 最强结合点。
运行时 props 写法也可以推断类型,但不如泛型写法强。
setup 返回值由 TS 自动推断,与宏无关。