Vue 4与TypeScript 5.0类型安全开发指南

160 阅读2分钟

Vue + TypeScript 类型安全开发实战

一、环境搭建

npm create vue@latest
# 选择 TypeScript + ESLint + 组合式API

tsconfig.json 关键配置:

{
  "compilerOptions": {
    "strict": true,
    "types": ["vite/client"],
    "experimentalDecorators": true,
    "useDefineForClassFields": true
  }
}

二、组件类型安全

1. Props 类型定义

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

defineProps<{
  user: User
  isActive: boolean
  // 带默认值需特殊处理
  count?: number
}>()

withDefaults(defineProps<{
  count?: number
}>(), {
  count: 0
})

2. Emits 类型声明

const emit = defineEmits<{
  (e: 'update:modelValue', value: string): void
  (e: 'submit', payload: User): Promise<boolean>
}>()

三、组合式 API 类型增强

1. 响应式数据

const count = ref<number>(0) // 明确类型
const user = reactive<User>({
  id: 1,
  name: 'John'
})

// 自动推断
const double = computed(() => count.value * 2)

2. 自定义 Hook

// useFetch.ts
export function useFetch<T>(url: string) {
  const data = ref<T | null>(null)
  const error = ref<Error | null>(null)

  const fetchData = async () => {
    try {
      const response = await fetch(url)
      data.value = await response.json() as T
    } catch (err) {
      error.value = err as Error
    }
  }

  return { data, error, fetchData }
}

// 组件中使用
const { data: posts } = useFetch<Post[]>('/api/posts')

四、TypeScript 5.0 新特性应用

1. 泛型参数常量

function identity<const T>(arg: T): T {
  return arg
}

// 推导类型为 ["hello", 42] 而非 (string | number)[]
const result = identity(["hello", 42])

2. 装饰器元数据

@Reflect.metadata('design:type', Object)
class UserComponent {
  @Track()
  user: User = reactive(new User())
}

五、高级类型模式

1. 条件类型 Props

type Props<T extends 'text' | 'number'> = {
  type: T
  value: T extends 'text' ? string : number
}

defineProps<Props<'text'>>()

2. 类型安全的全局状态

// stores/counter.ts
export const useCounter = defineStore('counter', () => {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  
  function increment(step: number = 1) {
    count.value += step
  }

  return { count, double, increment }
})

// 组件中使用
const counter = useCounter()
counter.increment(2) // 类型检查参数

六、最佳实践

  1. 严格模式:始终开启 strict: true
  2. 类型推断:优先让 TypeScript 自动推断简单类型
  3. 泛型组件:使用 generic 特性声明泛型组件
<script setup lang="ts" generic="T extends string | number">
defineProps<{
  items: T[]
  selected: T
}>()
</script>
  1. 类型导入:使用 Type-Only Imports
import type { Router } from 'vue-router'

七、常见问题解决

1. 模板引用类型

<script setup lang="ts">
import { ref } from 'vue'
import type { ElForm } from 'element-plus'

const formRef = ref<InstanceType<typeof ElForm>>()
</script>

<template>
  <el-form ref="formRef"></el-form>
</template>

2. 动态组件类型

import { defineAsyncComponent } from 'vue'

const components = {
  home: defineAsyncComponent(() => import('./Home.vue')),
  profile: defineAsyncComponent(() => import('./Profile.vue'))
} as const // 关键类型断言

type ComponentKeys = keyof typeof components