Files
2026-07-13 21:37:07 +08:00

3.4 KiB
Raw Permalink Blame History

Vue TypeScript 模式

Vue 3 开发的 TypeScript 专属模式。

Provide/Inject 类型

使用 InjectionKey 实现类型安全的依赖注入:

import type { InjectionKey } from 'vue'
import type { User } from './types'

// 定义类型化的 key
export const UserKey: InjectionKey<User> = Symbol('user')

// 提供者组件
const user = ref<User>({ id: 1, name: 'John' })
provide(UserKey, user)

// 消费者组件
const user = inject(UserKey) // Ref<User> | undefined
const user = inject(UserKey)! // Ref<User>(断言非空)

使用默认值:

const user = inject(UserKey, ref({ id: 0, name: 'Guest' }))
// 类型:Ref<User>(无 undefined

vue-tsc 严格模板

启用更严格的模板类型检查:

# 以严格模式检查模板
vue-tsc --noEmit --strict-templates

可捕获如下模板错误:

  • 访问不存在的属性
  • 错误的 prop 类型
  • 缺少必需的 props

tsconfig 设置

Vue 3 必需项:

{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "verbatimModuleSyntax": true,
    "strict": true,
    "jsx": "preserve"
  }
}

moduleResolution: "bundler" - 与 Vite/webpack 的解析方式匹配。避免 .js 扩展名问题。

verbatimModuleSyntax: true - 强制使用显式 type 导入:

// ❌ 可能导致打包工具出现问题
import { User } from './types'

// ✅ 显式类型导入
import type { User } from './types'

组件类型辅助工具

从组件中提取 props 类型:

import type { ComponentProps, ComponentSlots, ComponentEmits } from 'vue-component-type-helpers'
import MyComponent from './MyComponent.vue'

type Props = ComponentProps<typeof MyComponent>
type Slots = ComponentSlots<typeof MyComponent>
type Emits = ComponentEmits<typeof MyComponent>

提取暴露的方法:

import type { ComponentExposed } from 'vue-component-type-helpers'

type Exposed = ComponentExposed<typeof MyComponent>

泛型组件

使用类型化的 slots 定义泛型组件:

<script setup lang="ts" generic="T extends { id: string }">
defineProps<{
  items: T[]
}>()

defineSlots<{
  default: (props: { item: T }) => any
}>()
</script>

<template>
  <div v-for="item in items" :key="item.id">
    <slot :item="item" />
  </div>
</template>

Ref 类型收窄

正确处理 ref 类型收窄:

const maybeUser = ref<User | null>(null)

// ❌ TypeScript 仍然视为 User | null
if (maybeUser.value) {
  maybeUser.value.name // 错误:可能为 null
}

// ✅ 使用 computed 或提取值
const userName = computed(() => maybeUser.value?.name ?? 'Guest')

// ✅ 或在同一表达式中守卫
maybeUser.value && maybeUser.value.name

事件处理器类型

正确为事件处理器添加类型:

// DOM 事件
const onClick = (e: MouseEvent) => { ... }
const onInput = (e: Event) => {
  const target = e.target as HTMLInputElement
  console.log(target.value)
}

// 组件 emits
const onUpdate = (value: string) => { ... }

常见错误

忘记显式导入类型:

// ❌ 运行时导入本应是纯类型的内容
import { User } from './types'

// ✅ 纯类型导入
import type { User } from './types'

未对字面量类型使用 as const

// ❌ 类型为 string[]
const variants = ['primary', 'secondary']

// ✅ 类型为 readonly ['primary', 'secondary']
const variants = ['primary', 'secondary'] as const