Vue 3 原生支持 TypeScript,<script setup lang="ts"> 让 TypeScript 成为 Vue 组件的一等公民。本文覆盖从 Props 类型定义到 Pinia Store 类型安全的完整实践。
一、组件 Props 类型定义
1.1 defineProps(推荐写法)
<script setup lang="ts">
interface Props {
title: string
count?: number
items: Item[]
onUpdate?: (id: string) => void
}
// 运行时 + 编译时类型
const props = defineProps<Props>()
// 带默认值(使用 withDefaults)
const props = withDefaults(defineProps<Props>(), {
count: 0,
onUpdate: () => {},
})
</script>
1.2 运行时 Props(需要运行时验证时)
<script setup lang="ts">
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 },
items: { type: Array as PropType<Item[]>, required: true },
})
</script>
1.3 defineEmits 类型化
<script setup lang="ts">
const emit = defineEmits<{
update: [value: string] // 事件名 + 参数类型
delete: [id: number]
'update:modelValue': [val: boolean] // v-model 事件
}>()
// 使用
emit('update', 'new value')
</script>
二、响应式类型
2.1 ref 类型
import { ref } from 'vue'
const count = ref<number>(0) // Ref<number>
const user = ref<User | null>(null) // Ref<User | null>
const list = ref<string[]>([]) // Ref<string[]>
// 复杂对象
interface State {
loading: boolean
data: Data | null
error: Error | null
}
const state = ref<State>({
loading: false,
data: null,
error: null,
})
2.2 reactive 类型
interface FormState {
email: string
password: string
errors: Record<string, string>
}
const form = reactive<FormState>({
email: '',
password: '',
errors: {},
})
// form.email 类型为 string
// form.errors 类型为 Record<string, string>
2.3 computed 类型
const user = ref<User | null>(null)
// 类型自动推断为 string | undefined
const displayName = computed(() => user.value?.name?.toUpperCase())
// 显式声明返回类型
const userList = computed<User[]>(() => {
return userStore.users.filter(u => u.active)
})
三、Pinia 类型安全
// stores/auth.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
interface User {
id: string
name: string
email: string
role: 'admin' | 'user' | 'guest'
}
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null)
const token = ref<string>('')
const isLoggedIn = computed(() => !!user.value)
const isAdmin = computed(() => user.value?.role === 'admin')
async function login(email: string, password: string): Promise<void> {
const res = await api.login({ email, password })
user.value = res.user
token.value = res.token
}
function logout(): void {
user.value = null
token.value = ''
}
return { user, token, isLoggedIn, isAdmin, login, logout }
})
// 使用时类型自动推导
const auth = useAuthStore()
auth.user?.name // string | undefined
auth.login('a@b.com', 'pwd') // 参数类型检查
四、API 数据契约(Zod)
// schemas/user.ts
import { z } from 'zod'
export const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1),
email: z.string().email(),
role: z.enum(['admin', 'user', 'guest']),
createdAt: z.string().datetime(),
})
export type User = z.infer<typeof UserSchema>
// API 层
export async function fetchUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`)
const json = await res.json()
return UserSchema.parse(json) // 运行时验证
}
五、tsconfig.json 推荐配置
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.vue"]
}
常见问题(FAQ)
<script setup lang="ts"> 报错找不到模块?
确保 env.d.ts 或 shims-vue.d.ts 中有:
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
Props 解构如何保持响应性?
<script setup lang="ts">
const { title, count = 0 } = defineProps<Props>() // Vue 3.4+ 支持解构
// title 和 count 是响应式的(编译时转换)
</script>
相关阅读
- Vue 详解 — 核心概念
- Vue Composition API 完全指南 — TypeScript 模式
- React + TypeScript 实战指南 — 跨框架对比
- Zod 官方文档
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。
「frontend」更多文章
Vue 性能优化指南:虚拟列表、懒加载、渲染优化与 Core Web Vitals
Vue 3 应用性能优化完整策略:虚拟滚动(vue-virtual-scroller)、组件懒加载与异步组件、KeepAlive 缓存、Suspense 异步优化、v-memo 渲染记忆化、响应式性能(shallowRef/toRaw)、Bundle 分析与代码分割、渲染函数优化、以及 Core Web Vitals(LCP/INP/CLS)调优。
Nuxt.js 完全指南:Vue 全栈框架的 SSR、SSG、API 路由与部署实践
Nuxt.js 3 深度实践:文件系统路由、SSR/SSG/ISR 渲染模式、API 路由(Server Routes)、useFetch/useAsyncData 数据获取、中间件与插件、SEO 与 Meta 管理、Nitro 服务端引擎、自动导入、部署到 Vercel/Netlify/Node.js。
Vue 测试深度指南:Vitest + Vue Test Utils + Playwright E2E 与 CI 集成
Vue 3 应用从单元测试到 E2E 的完整测试策略:Vitest + Vue Test Utils 组件测试(mount/emits/slots/async)、Pinia Store Mocking、MSW API 拦截、Playwright 端到端测试、Cypress 组件测试、覆盖率标准与 GitHub Actions CI 集成。