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