Vue与TypeScript #
一、TypeScript配置 #
1.1 安装依赖 #
bash
npm install -D typescript
1.2 tsconfig.json #
json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
1.3 类型声明文件 #
typescript
// env.d.ts
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
二、组件类型定义 #
2.1 使用script setup #
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
// 带类型的ref
const count = ref<number>(0)
// 带类型的computed
const doubled = computed<number>(() => count.value * 2)
// 普通变量自动推断
const title = 'Hello TypeScript'
function increment(): void {
count.value++
}
</script>
2.2 defineComponent #
vue
<script lang="ts">
import { defineComponent, ref, PropType } from 'vue'
interface User {
id: number
name: string
email: string
}
export default defineComponent({
props: {
user: {
type: Object as PropType<User>,
required: true
}
},
setup(props) {
const count = ref<number>(0)
function increment(): void {
count.value++
}
return {
count,
increment
}
}
})
</script>
三、Props类型定义 #
3.1 基本类型 #
vue
<script setup lang="ts">
defineProps<{
title: string
count?: number
disabled?: boolean
}>()
</script>
3.2 带默认值 #
vue
<script setup lang="ts">
interface Props {
title: string
count?: number
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
count: 0,
disabled: false
})
</script>
3.3 复杂类型 #
vue
<script setup lang="ts">
interface User {
id: number
name: string
email: string
}
interface Props {
user: User
users: User[]
callback: (id: number) => void
}
defineProps<Props>()
</script>
3.4 使用PropType #
vue
<script lang="ts">
import { defineComponent, PropType } from 'vue'
interface User {
id: number
name: string
}
export default defineComponent({
props: {
user: {
type: Object as PropType<User>,
required: true
},
users: {
type: Array as PropType<User[]>,
default: () => []
},
status: {
type: String as PropType<'active' | 'inactive'>,
default: 'active'
}
}
})
</script>
四、Emits类型定义 #
4.1 类型化Emits #
vue
<script setup lang="ts">
interface Emits {
(e: 'update', value: string): void
(e: 'delete', id: number): void
(e: 'change', event: Event): void
}
const emit = defineEmits<Emits>()
function handleUpdate() {
emit('update', 'new value')
}
function handleDelete() {
emit('delete', 1)
}
</script>
4.2 简写形式 #
vue
<script setup lang="ts">
const emit = defineEmits<{
update: [value: string]
delete: [id: number]
change: [event: Event]
}>()
emit('update', 'value')
emit('delete', 1)
</script>
五、ref和reactive类型 #
5.1 ref类型 #
vue
<script setup lang="ts">
import { ref, Ref } from 'vue'
// 显式类型
const count: Ref<number> = ref(0)
const name = ref<string>('张三')
// 类型推断
const message = ref('Hello') // Ref<string>
// 复杂类型
interface User {
id: number
name: string
}
const user = ref<User | null>(null)
const users = ref<User[]>([])
</script>
5.2 reactive类型 #
vue
<script setup lang="ts">
import { reactive } from 'vue'
interface State {
count: number
name: string
loading: boolean
}
const state = reactive<State>({
count: 0,
name: '',
loading: false
})
// 类型推断
const state2 = reactive({
count: 0, // number
name: '' // string
})
</script>
5.3 computed类型 #
vue
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref(0)
// 自动推断
const doubled = computed(() => count.value * 2) // ComputedRef<number>
// 显式类型
const formatted = computed<string>(() => {
return `Count: ${count.value}`
})
</script>
六、组合式函数类型 #
6.1 定义类型化组合式函数 #
typescript
// composables/useUser.ts
import { ref, computed, type Ref, type ComputedRef } from 'vue'
interface User {
id: number
name: string
email: string
}
interface UseUserReturn {
user: Ref<User | null>
loading: Ref<boolean>
error: Ref<Error | null>
isLoggedIn: ComputedRef<boolean>
login: (credentials: { email: string; password: string }) => Promise<void>
logout: () => void
}
export function useUser(): UseUserReturn {
const user = ref<User | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
const isLoggedIn = computed(() => !!user.value)
async function login(credentials: { email: string; password: string }): Promise<void> {
loading.value = true
error.value = null
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
user.value = await response.json()
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
function logout(): void {
user.value = null
}
return {
user,
loading,
error,
isLoggedIn,
login,
logout
}
}
6.2 使用类型化组合式函数 #
vue
<script setup lang="ts">
import { useUser } from '@/composables/useUser'
const { user, loading, isLoggedIn, login } = useUser()
async function handleLogin() {
await login({
email: 'test@example.com',
password: 'password'
})
}
</script>
七、模板引用类型 #
7.1 DOM元素引用 #
vue
<template>
<input ref="inputRef">
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const inputRef = ref<HTMLInputElement | null>(null)
onMounted(() => {
inputRef.value?.focus()
})
</script>
7.2 组件引用 #
vue
<template>
<MyComponent ref="componentRef" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'
// 获取组件暴露的类型
type MyComponentExpose = InstanceType<typeof MyComponent>
const componentRef = ref<MyComponentExpose | null>(null)
function callMethod() {
componentRef.value?.someMethod()
}
</script>
八、API请求类型 #
8.1 封装请求函数 #
typescript
// utils/request.ts
interface RequestConfig {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
headers?: Record<string, string>
body?: any
}
interface ApiResponse<T> {
data: T
message: string
code: number
}
export async function request<T>(
url: string,
config: RequestConfig = {}
): Promise<ApiResponse<T>> {
const response = await fetch(url, {
method: config.method || 'GET',
headers: {
'Content-Type': 'application/json',
...config.headers
},
body: config.body ? JSON.stringify(config.body) : undefined
})
return response.json()
}
// 使用
interface User {
id: number
name: string
}
const result = await request<User>('/api/user/1')
console.log(result.data.name)
8.2 类型化API模块 #
typescript
// api/user.ts
import { request } from '@/utils/request'
export interface User {
id: number
name: string
email: string
}
export interface LoginParams {
email: string
password: string
}
export interface LoginResult {
token: string
user: User
}
export const userApi = {
async getUser(id: number): Promise<User> {
const res = await request<User>(`/api/users/${id}`)
return res.data
},
async login(params: LoginParams): Promise<LoginResult> {
const res = await request<LoginResult>('/api/login', {
method: 'POST',
body: params
})
return res.data
},
async updateUser(id: number, data: Partial<User>): Promise<User> {
const res = await request<User>(`/api/users/${id}`, {
method: 'PUT',
body: data
})
return res.data
}
}
九、总结 #
TypeScript集成要点 #
| 功能 | 类型定义方式 |
|---|---|
| Props | defineProps<{}>() |
| Emits | defineEmits<{}>() |
| ref | ref<Type>() |
| reactive | reactive<Type>() |
| computed | computed<Type>() |
| 组合式函数 | 返回类型接口 |
最佳实践:
- 使用
lang="ts"启用TypeScript - 为Props和Emits定义接口
- 组合式函数返回类型化对象
- 使用泛型封装通用函数
- 合理使用类型推断
最后更新:2026-03-26