Skip to content
第 20 章 前端 ⏱ 13 分钟阅读

第 20 章:TypeScript + Vue ​

学习目标 ​

  • 写 props / emit 的 TS 类型
  • 用 defineProps<{}> 配合默认值
  • 用 defineModel 简化 v-model
  • 避坑:模板 ref 的类型

一、组件 props 类型 ​

vue
<script setup lang="ts">
defineProps<{
  name: string
  age?: number
  tags?: string[]
  user: { id: number; name: string }
}>()
</script>

带默认值:

ts
withDefaults(
  defineProps<{ name?: string; age?: number }>(),
  { name: '匿名', age: 18 },
)

二、emit 类型 ​

ts
const emit = defineEmits<{
  (e: 'change', val: string): void
  (e: 'update:modelValue', val: number): void
}>()

Vue 3.3+ 简写:

ts
const emit = defineEmits<{
  change: [val: string]
  'update:modelValue': [val: number]
}>()

三、defineModel(Vue 3.4+) ​

vue
<script setup lang="ts">
const model = defineModel<string>()                // 默认 modelValue
const title = defineModel<string>('title')        // 命名 model
</script>

<template>
  <input v-model="model" />
  <input v-model="title" />
</template>
vue
<MyInput v-model="name" v-model:title="t" />

四、模板 ref 类型 ​

vue
<script setup lang="ts">
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'

const inputRef = ref<HTMLInputElement | null>(null)
const compRef = ref<InstanceType<typeof MyComponent> | null>(null)

onMounted(() => {
  inputRef.value?.focus()
  compRef.value?.reset()
})
</script>

<template>
  <input ref="inputRef" />
  <MyComponent ref="compRef" />
</template>

⚠️ 坑 1:模板 ref 默认为 any,Vue 3.5+ 才是 Ref<unknown>,必须显式标注。

五、组件实例类型 ​

ts
import MyComponent from './MyComponent.vue'

const ref1 = ref<InstanceType<typeof MyComponent> | null>(null)

子组件用 defineExpose 暴露字段:

ts
// MyComponent.vue
defineExpose({ reset, count })

六、泛型组件 ​

vue
<script setup lang="ts" generic="T extends { id: string }">
defineProps<{ items: T[]; onSelect: (item: T) => void }>()
</script>
vue
<Picker :items="users" :on-select="select" />

⚠️ 坑 2:generic 是 Vue 3.3+ 特性,3.2 不支持。

七、reactive 对象类型 ​

ts
import { reactive, ref } from 'vue'

interface User {
  id: number
  name: string
  age?: number
}

const user = reactive<User>({ id: 1, name: 'A' })
const users = ref<User[]>([])

八、Pinia store 类型 ​

ts
export const useUserStore = defineStore('user', () => {
  const name = ref('')
  const token = ref('')
  return { name, token }
})
vue
const store = useUserStore()
store.name = 'A'           // ✅ 类型推断

💡 ref('') TS 自动推断成 Ref<string>,不用标;只有初值是 null / 空数组 / 联合类型时才需要 ref<Xxx>(...)。

九、工具类型 ​

ts
import type { ComponentCustomProperties, Ref } from 'vue'

declare module 'vue' {
  interface ComponentCustomProperties {
    $api: typeof api
  }
}
ts
app.config.globalProperties.$api = api

十、本章小结 ​

能力语法
PropsdefineProps<{}>()
EmitsdefineEmits<{}>()
v-modeldefineModel<T>()
Refref<HTMLInputElement | null>(null)
组件实例InstanceType<typeof X>

动手练习 ​

  1. 泛型列表:写 List<T>,接收 items 和 render
  2. defineModel:封装一个 number input v-model
  3. Ref 类型化:用 TS 拿组件实例,调用 expose 出的方法

下一章:第 21 章:组件库 →

本站基于 VitePress 构建 · 由 StackHub 团队维护