Skip to content

📝 TypeScript 使用指南 - 類型安全的開發之旅!

🚀 想要告別 JavaScript 的「類型恐懼症」?這份 TypeScript 寶典將帶你在 Vant + Vue 3 的世界裡暢遊!從基礎配置到高級類型技巧,從組件開發到狀態管理,我們將一步步構建起堅不可摧的類型安全防線。準備好擁抱更智能、更安全的開發體驗了嗎?

🎯 TypeScript 優勢

核心優勢

  • 類型安全:編譯時錯誤檢查,減少運行時bug
  • 開發效率:智能提示和自動補全,提升開發體驗
  • 重構友好:安全的代碼重構,降低維護成本
  • 文檔化:類型即文檔,提升代碼可讀性

🚀 快速開始

項目初始化

bash
# 使用 Vite 官方模板
npm create vue@latest my-vant-ts-app

# 交互式選擇配置
 Project name: my-vant-ts-app
 Add TypeScript? Yes
 Add JSX Support? No  
 Add Vue Router? Yes
 Add Pinia? Yes
 Add Vitest? Yes
 Add ESLint? Yes
 Add Prettier? Yes

# 安裝 Vant 依賴
cd my-vant-ts-app
npm install vant @vant/touch-emulator

# 安裝開發依賴
npm install -D @types/node unplugin-vue-components

⚙️ 核心配置

Vite 配置

typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'
import { resolve } from 'path'

export default defineConfig({
  plugins: [
    vue({
      script: {
        defineModel: true,
        propsDestructure: true
      }
    }),
    Components({
      resolvers: [VantResolver()],
      dts: true,
      dirs: ['src/components'],
      extensions: ['vue', 'ts'],
      deep: true
    })
  ],
  
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@components': resolve(__dirname, 'src/components'),
      '@utils': resolve(__dirname, 'src/utils'),
      '@types': resolve(__dirname, 'src/types'),
      '@api': resolve(__dirname, 'src/api')
    }
  },
  
  server: {
    port: 3000,
    open: true,
    cors: true
  },
  
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vue-vendor': ['vue', 'vue-router', 'pinia'],
          'vant-vendor': ['vant']
        }
      }
    }
  }
})

TypeScript 配置

json
{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": [
    "env.d.ts",
    "src/**/*",
    "src/**/*.vue",
    "tests/**/*"
  ],
  "exclude": [
    "dist",
    "node_modules"
  ],
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "moduleResolution": "bundler",
    
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"],
      "@types/*": ["src/types/*"],
      "@api/*": ["src/api/*"]
    },
    
    "jsx": "preserve",
    "jsxImportSource": "vue",
    
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "isolatedModules": true,
    "verbatimModuleSyntax": true,
    
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

環境類型聲明

typescript
// env.d.ts
/// <reference types="vite/client" />

declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

declare module 'vant' {
  export * from 'vant/es'
}

interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string
  readonly VITE_APP_VERSION: string
  readonly VITE_API_BASE_URL: string
  readonly VITE_APP_ENV: 'development' | 'production' | 'test'
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

declare global {
  interface Window {
    __VUE_DEVTOOLS_GLOBAL_HOOK__?: any
    gtag?: (...args: any[]) => void
    dataLayer?: any[]
  }
  
  interface DocumentEventMap {
    'app:ready': CustomEvent<{ version: string }>
    'user:login': CustomEvent<{ userId: string }>
  }
}

export {}

📝 類型系統設計

基礎業務類型

typescript
// types/user.ts
export interface BaseUser {
  readonly id: string
  name: string
  email: string
  avatar?: string
  createdAt: Date
  updatedAt: Date
}

export interface User extends BaseUser {
  phone?: string
  status: UserStatus
  role: UserRole
  profile: UserProfile
  preferences: UserPreferences
}

export type UserStatus = 'active' | 'inactive' | 'pending' | 'suspended'
export type UserRole = 'admin' | 'user' | 'guest' | 'moderator'

export interface UserProfile {
  firstName: string
  lastName: string
  bio?: string
  location?: string
  website?: string
  socialLinks: SocialLinks
}

export interface SocialLinks {
  github?: string
  twitter?: string
  linkedin?: string
}

export interface UserPreferences {
  theme: 'light' | 'dark' | 'auto'
  language: 'zh-CN' | 'en-US'
  notifications: NotificationSettings
}

export interface NotificationSettings {
  email: boolean
  push: boolean
  sms: boolean
  categories: NotificationCategory[]
}

export type NotificationCategory = 
  | 'system' 
  | 'security' 
  | 'marketing' 
  | 'updates'

// 類型守衛函數
export function isValidUser(obj: any): obj is User {
  return (
    typeof obj === 'object' &&
    typeof obj.id === 'string' &&
    typeof obj.name === 'string' &&
    typeof obj.email === 'string' &&
    obj.createdAt instanceof Date
  )
}

API 響應類型

typescript
// types/api.ts
export interface ApiResponse<T = unknown> {
  readonly code: number
  readonly message: string
  readonly data: T
  readonly timestamp: number
  readonly requestId: string
}

export interface ApiError {
  readonly code: number
  readonly message: string
  readonly details?: Record<string, any>
  readonly field?: string
}

export interface PaginationParams {
  page: number
  pageSize: number
  sortBy?: string
  sortOrder?: 'asc' | 'desc'
}

export interface PaginationResponse<T> {
  list: T[]
  total: number
  page: number
  pageSize: number
  totalPages: number
  hasNext: boolean
  hasPrev: boolean
}

export interface ListResponse<T> extends ApiResponse<PaginationResponse<T>> {}

export interface RequestConfig {
  timeout?: number
  retries?: number
  showLoading?: boolean
  showError?: boolean
  errorHandler?: (error: ApiError) => void
}

export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
export type RequestParams = Record<string, any>
export type RequestData = Record<string, any> | FormData

export type ResponseInterceptor<T = any> = (
  response: ApiResponse<T>
) => ApiResponse<T> | Promise<ApiResponse<T>>

組件 Props 類型

typescript
// types/components.ts
import type { VNode, Component } from 'vue'

export interface BaseComponentProps {
  id?: string
  class?: string | string[] | Record<string, boolean>
  style?: string | Record<string, string | number>
}

export type ComponentSize = 'mini' | 'small' | 'normal' | 'large'
export type ComponentTheme = 
  | 'primary' 
  | 'success' 
  | 'warning' 
  | 'danger' 
  | 'default'

export interface ButtonProps extends BaseComponentProps {
  type?: ComponentTheme
  size?: ComponentSize
  loading?: boolean
  disabled?: boolean
  round?: boolean
  square?: boolean
  icon?: string | Component
  iconPosition?: 'left' | 'right'
  nativeType?: 'button' | 'submit' | 'reset'
  block?: boolean
  text?: boolean
  color?: string
  loadingText?: string
  loadingType?: 'circular' | 'spinner'
  loadingSize?: string | number
}

export interface FormItemProps extends BaseComponentProps {
  label?: string
  name?: string
  required?: boolean
  rules?: ValidationRule[]
  error?: string
  labelWidth?: string | number
  labelAlign?: 'left' | 'center' | 'right'
  inputAlign?: 'left' | 'center' | 'right'
  colon?: boolean
}

export interface ValidationRule {
  required?: boolean
  message?: string
  pattern?: RegExp
  min?: number
  max?: number
  len?: number
  validator?: (value: any, rule: ValidationRule) => boolean | string | Promise<boolean | string>
  trigger?: 'onChange' | 'onBlur' | 'onSubmit'
}

export type EventHandler<T = Event> = (event: T) => void
export type AsyncEventHandler<T = Event> = (event: T) => Promise<void>

export type SlotContent = string | number | VNode | VNode[]
export type ScopedSlot<T = any> = (props: T) => SlotContent

🎯 Vue 3 + Vant 類型實踐

高級組件類型定義

vue
<!-- UserCard.vue -->
<script setup lang="ts">
import type { User, UserStatus } from '@/types/user'
import type { ComponentSize } from '@/types/components'

interface Props {
  user: User
  size?: ComponentSize
  showActions?: boolean
  showStatus?: boolean
  clickable?: boolean
  loading?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  size: 'normal',
  showActions: true,
  showStatus: true,
  clickable: false,
  loading: false
})

interface Emits {
  (e: 'click', user: User): void
  (e: 'edit', userId: string): void
  (e: 'delete', userId: string): void
  (e: 'statusChange', userId: string, status: UserStatus): void
}

const emit = defineEmits<Emits>()

const displayName = computed((): string => {
  const { firstName, lastName } = props.user.profile
  return `${firstName} ${lastName}`.trim() || props.user.name
})

const statusColor = computed((): string => {
  const statusMap: Record<UserStatus, string> = {
    active: '#07c160',
    inactive: '#969799',
    pending: '#ff976a',
    suspended: '#ee0a24'
  }
  return statusMap[props.user.status]
})

const cardClass = computed(() => [
  'user-card',
  `user-card--${props.size}`,
  {
    'user-card--clickable': props.clickable,
    'user-card--loading': props.loading
  }
])

const handleClick = (): void => {
  if (props.clickable && !props.loading) {
    emit('click', props.user)
  }
}

const handleEdit = (event: Event): void => {
  event.stopPropagation()
  emit('edit', props.user.id)
}

const handleDelete = (event: Event): void => {
  event.stopPropagation()
  emit('delete', props.user.id)
}

const handleStatusChange = (newStatus: UserStatus): void => {
  emit('statusChange', props.user.id, newStatus)
}

defineExpose({
  refresh: () => {
    // 刷新用戶數據的邏輯
  }
})
</script>

<template>
  <van-card
    :class="cardClass"
    :title="displayName"
    :desc="user.email"
    :thumb="user.avatar"
    @click="handleClick"
  >
    <template #tags v-if="showStatus">
      <van-tag 
        :color="statusColor" 
        size="small"
        @click="handleStatusChange"
      >
        {{ user.status }}
      </van-tag>
    </template>
    
    <template #footer v-if="showActions">
      <div class="user-card__actions">
        <van-button 
          size="small" 
          type="primary"
          :loading="loading"
          @click="handleEdit"
        >
          編輯
        </van-button>
        <van-button 
          size="small" 
          type="danger"
          :loading="loading"
          @click="handleDelete"
        >
          刪除
        </van-button>
      </div>
    </template>
  </van-card>
</template>

<style scoped lang="scss">
.user-card {
  &--clickable {
    cursor: pointer;
    transition: all 0.3s ease;
    
    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    }
  }
  
  &--loading {
    opacity: 0.6;
    pointer-events: none;
  }
  
  &__actions {
    display: flex;
    gap: 8px;
  }
}
</style>

狀態管理類型定義

typescript
// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { User, UserStatus } from '@/types/user'
import type { ApiResponse, PaginationParams, ListResponse } from '@/types/api'
import { userApi } from '@/api/user'

interface UserState {
  currentUser: User | null
  users: User[]
  loading: boolean
  error: string | null
  pagination: {
    page: number
    pageSize: number
    total: number
  }
}

export const useUserStore = defineStore('user', () => {
  // 狀態定義
  const currentUser = ref<User | null>(null)
  const users = ref<User[]>([])
  const loading = ref<boolean>(false)
  const error = ref<string | null>(null)
  const pagination = ref({
    page: 1,
    pageSize: 20,
    total: 0
  })

  // 計算屬性 (Getters)
  const isLoggedIn = computed((): boolean => {
    return currentUser.value !== null
  })

  const activeUsers = computed((): User[] => {
    return users.value.filter(user => user.status === 'active')
  })

  const userCount = computed((): number => {
    return users.value.length
  })

  const hasNextPage = computed((): boolean => {
    const { page, pageSize, total } = pagination.value
    return page * pageSize < total
  })

  const hasPrevPage = computed((): boolean => {
    return pagination.value.page > 1
  })

  // Actions
  const fetchUsers = async (params?: PaginationParams): Promise<void> => {
    try {
      loading.value = true
      error.value = null

      const response: ListResponse<User> = await userApi.getUsers(params)

      if (response.code === 200) {
        users.value = response.data.list
        pagination.value = {
          page: response.data.page,
          pageSize: response.data.pageSize,
          total: response.data.total
        }
      } else {
        throw new Error(response.message)
      }
    } catch (err) {
      error.value = err instanceof Error ? err.message : '獲取用戶列表失敗'
      console.error('fetchUsers error:', err)
    } finally {
      loading.value = false
    }
  }

  const fetchUserById = async (id: string): Promise<User | null> => {
    try {
      loading.value = true
      const response: ApiResponse<User> = await userApi.getUserById(id)
      
      if (response.code === 200) {
        return response.data
      } else {
        throw new Error(response.message)
      }
    } catch (err) {
      error.value = err instanceof Error ? err.message : '獲取用戶詳情失敗'
      return null
    } finally {
      loading.value = false
    }
  }

  const createUser = async (
    userData: Omit<User, 'id' | 'createdAt' | 'updatedAt'>
  ): Promise<boolean> => {
    try {
      loading.value = true
      const response: ApiResponse<User> = await userApi.createUser(userData)

      if (response.code === 200) {
        users.value.unshift(response.data)
        pagination.value.total += 1
        return true
      } else {
        throw new Error(response.message)
      }
    } catch (err) {
      error.value = err instanceof Error ? err.message : '創建用戶失敗'
      return false
    } finally {
      loading.value = false
    }
  }

  const updateUser = async (id: string, userData: Partial<User>): Promise<boolean> => {
    try {
      loading.value = true
      const response: ApiResponse<User> = await userApi.updateUser(id, userData)

      if (response.code === 200) {
        const index = users.value.findIndex(user => user.id === id)
        if (index !== -1) {
          users.value[index] = response.data
        }
        if (currentUser.value?.id === id) {
          currentUser.value = response.data
        }
        return true
      } else {
        throw new Error(response.message)
      }
    } catch (err) {
      error.value = err instanceof Error ? err.message : '更新用戶失敗'
      return false
    } finally {
      loading.value = false
    }
  }

  const deleteUser = async (id: string): Promise<boolean> => {
    try {
      loading.value = true
      const response: ApiResponse<void> = await userApi.deleteUser(id)

      if (response.code === 200) {
        users.value = users.value.filter(user => user.id !== id)
        pagination.value.total -= 1
        return true
      } else {
        throw new Error(response.message)
      }
    } catch (err) {
      error.value = err instanceof Error ? err.message : '刪除用戶失敗'
      return false
    } finally {
      loading.value = false
    }
  }

  const setCurrentUser = (user: User | null): void => {
    currentUser.value = user
  }

  const updateUserStatus = async (id: string, status: UserStatus): Promise<boolean> => {
    return updateUser(id, { status })
  }

  const clearError = (): void => {
    error.value = null
  }

  const reset = (): void => {
    currentUser.value = null
    users.value = []
    loading.value = false
    error.value = null
    pagination.value = { page: 1, pageSize: 20, total: 0 }
  }

  return {
    // 狀態
    currentUser: readonly(currentUser),
    users: readonly(users),
    loading: readonly(loading),
    error: readonly(error),
    pagination: readonly(pagination),

    // 計算屬性
    isLoggedIn,
    activeUsers,
    userCount,
    hasNextPage,
    hasPrevPage,

    // 方法
    fetchUsers,
    fetchUserById,
    createUser,
    updateUser,
    deleteUser,
    setCurrentUser,
    updateUserStatus,
    clearError,
    reset
  }
})

export type UserStore = ReturnType<typeof useUserStore>

🔧 API 請求類型封裝

類型安全的 HTTP 客戶端

typescript
// utils/request.ts
import axios, { 
  AxiosInstance, 
  AxiosRequestConfig, 
  AxiosResponse, 
  AxiosError 
} from 'axios'
import type { ApiResponse, ApiError, RequestConfig } from '@/types/api'
import { showToast, showLoadingToast, closeToast } from 'vant'

interface ExtendedAxiosRequestConfig extends AxiosRequestConfig {
  showLoading?: boolean
  showError?: boolean
  retries?: number
  retryDelay?: number
}

class HttpClient {
  private instance: AxiosInstance
  private loadingCount = 0

  constructor(baseURL: string) {
    this.instance = axios.create({
      baseURL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json'
      }
    })

    this.setupInterceptors()
  }

  private setupInterceptors(): void {
    // 請求攔截器
    this.instance.interceptors.request.use(
      (config: ExtendedAxiosRequestConfig) => {
        if (config.showLoading) {
          this.showLoading()
        }

        const token = localStorage.getItem('token')
        if (token) {
          config.headers = {
            ...config.headers,
            Authorization: `Bearer ${token}`
          }
        }

        return config
      },
      (error: AxiosError) => {
        this.hideLoading()
        return Promise.reject(error)
      }
    )

    // 響應攔截器
    this.instance.interceptors.response.use(
      (response: AxiosResponse<ApiResponse>) => {
        this.hideLoading()
        
        const { data } = response
        
        if (data.code !== 200) {
          const error: ApiError = {
            code: data.code,
            message: data.message
          }
          
          if (response.config.showError !== false) {
            showToast(error.message)
          }
          
          return Promise.reject(error)
        }

        return response
      },
      async (error: AxiosError) => {
        this.hideLoading()
        
        const config = error.config as ExtendedAxiosRequestConfig
        
        if (config?.retries && config.retries > 0) {
          config.retries -= 1
          await this.delay(config.retryDelay || 1000)
          return this.instance.request(config)
        }

        const apiError: ApiError = {
          code: error.response?.status || 0,
          message: this.getErrorMessage(error)
        }

        if (config?.showError !== false) {
          showToast(apiError.message)
        }

        return Promise.reject(apiError)
      }
    )
  }

  private showLoading(): void {
    if (this.loadingCount === 0) {
      showLoadingToast({
        message: '加載中...',
        forbidClick: true,
        duration: 0
      })
    }
    this.loadingCount++
  }

  private hideLoading(): void {
    this.loadingCount--
    if (this.loadingCount <= 0) {
      this.loadingCount = 0
      closeToast()
    }
  }

  private getErrorMessage(error: AxiosError): string {
    if (error.response) {
      const status = error.response.status
      const statusMessages: Record<number, string> = {
        400: '請求參數錯誤',
        401: '未授權,請重新登錄',
        403: '拒絕訪問',
        404: '請求的資源不存在',
        408: '請求超時',
        500: '服務器內部錯誤',
        502: '網關錯誤',
        503: '服務不可用',
        504: '網關超時'
      }
      return statusMessages[status] || `請求失敗 (${status})`
    } else if (error.request) {
      return '網絡連接失敗,請檢查網絡'
    } else {
      return error.message || '未知錯誤'
    }
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }

  async request<T = any>(config: ExtendedAxiosRequestConfig): Promise<ApiResponse<T>> {
    const response = await this.instance.request<ApiResponse<T>>(config)
    return response.data
  }

  async get<T = any>(
    url: string,
    params?: any,
    config?: RequestConfig
  ): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'GET',
      url,
      params,
      ...config
    })
  }

  async post<T = any>(
    url: string,
    data?: any,
    config?: RequestConfig
  ): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'POST',
      url,
      data,
      ...config
    })
  }

  async put<T = any>(
    url: string,
    data?: any,
    config?: RequestConfig
  ): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'PUT',
      url,
      data,
      ...config
    })
  }

  async delete<T = any>(
    url: string,
    config?: RequestConfig
  ): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'DELETE',
      url,
      ...config
    })
  }
}

const httpClient = new HttpClient(import.meta.env.VITE_API_BASE_URL)

export default httpClient

📚 最佳實踐總結 - TypeScript 高手的修煉之道

類型安全檢查清單 - 成為類型大師的必備技能 ✨

  • 啟用嚴格模式 🔒:strict: true 是你的安全網(拒絕任何類型漏洞)
  • 類型導入規範 📦:使用 import type 導入類型(編譯時優化的秘密武器)
  • 泛型的藝術 🎨:合理使用泛型和類型約束(讓代碼既靈活又安全)
  • 運行時守護 🛡️:實現運行時類型檢查函數(雙重保險更安心)
  • 不可變數據 🔐:使用 readonly 保護數據(防止意外修改)
  • 聯合類型優先 🤝:使用聯合類型替代 any(精確描述每種可能)
  • 工具類型大師 🔧:善用 Partial、Pick、Omit 等(類型操作的瑞士軍刀)
  • 類型斷言謹慎 ⚠️:優先使用類型守衛(安全第一,斷言第二)

TypeScript 最佳實踐詳解 🎯

  1. 啟用嚴格模式

    json
    {
      "compilerOptions": {
        "strict": true,
        "noImplicitAny": true,
        "strictNullChecks": true
      }
    }
  2. 使用類型導入

    typescript
    // ✅ 推薦
    import type { User } from '@/types/user'
    import { createUser } from '@/api/user'
    
    // ❌ 避免
    import { User, createUser } from '@/api/user'
  3. 善用泛型

    typescript
    // ✅ 靈活的泛型函數
    function createApiResponse<T>(data: T): ApiResponse<T> {
      return {
        code: 200,
        message: 'success',
        data,
        timestamp: Date.now()
      }
    }
  4. 運行時類型守衛

    typescript
    function isUser(obj: any): obj is User {
      return obj && 
             typeof obj.id === 'string' &&
             typeof obj.name === 'string' &&
             typeof obj.email === 'string'
    }
  5. 使用 readonly 保護數據

    typescript
    interface ReadonlyUser {
      readonly id: string
      readonly email: string
      readonly profile: Readonly<UserProfile>
    }
  6. 聯合類型和字面量類型

    typescript
    type Theme = 'light' | 'dark' | 'auto'
    type ButtonSize = 'small' | 'medium' | 'large'
  7. 工具類型的使用

    typescript
    // 從現有類型創建新類型
    type CreateUserRequest = Omit<User, 'id' | 'createdAt' | 'updatedAt'>
    type UpdateUserRequest = Partial<Pick<User, 'name' | 'email' | 'profile'>>
    type UserKeys = keyof User
  8. 避免 any,使用 unknown

    typescript
    // ✅ 推薦
    function processData(data: unknown): string {
      if (typeof data === 'string') {
        return data.toUpperCase()
      }
      return String(data)
    }
    
    // ❌ 避免
    function processData(data: any): string {
      return data.toUpperCase()
    }

性能優化建議 - 讓 TypeScript 飛起來 🚀

編譯性能優化

  • 使用 skipLibCheck 跳過庫類型檢查(節省編譯時間)
  • 合理配置 include/exclude(只檢查需要的文件)
  • 使用增量編譯(只編譯變更部分)
  • 避免過深的類型嵌套(編譯器也需要喘口氣)

打包優化策略 📦

  • 類型文件不會影響運行時(零運行時開銷)
  • 使用 Tree Shaking 移除未使用代碼(輕裝上陣)
  • 合理分包避免類型污染(模塊化的力量)
  • 生產環境移除類型檢查(專注性能)
  1. 編譯性能優化

    • 使用 incremental 編譯
    • 配置 skipLibCheck: true
    • 合理使用 includeexclude
  2. 打包優化

    • 啟用 Tree Shaking
    • 使用動態導入進行代碼分割
    • 優化類型聲明文件的生成

💡 進階技巧 - TypeScript 忍者的秘密武器

  1. 類型編程 🧠

    • 使用條件類型實現複雜邏輯(類型級別的 if-else)
    • 模板字面量類型創建動態類型(字符串也能有類型)
  2. 聲明合併 🔗

    • 擴展第三方庫類型(讓庫更符合你的需求)
    • 模塊增強技術(給現有模塊添加新功能)
  3. 類型體操 🤸

    • 遞歸類型處理複雜結構(類型的遞歸之美)
    • 映射類型批量操作(一次性處理多個屬性)
  4. 調試技巧 🔍

    • 使用 TypeScript Playground 驗證類型(在線實驗室)
    • 善用 IDE 的類型提示(讓編輯器成為你的助手)

高級 TypeScript 技巧

  1. 類型編程

    typescript
    // 條件類型
    type NonNullable<T> = T extends null | undefined ? never : T
    
    // 映射類型
    type Optional<T> = {
      [K in keyof T]?: T[K]
    }
    
    // 模板字面量類型
    type EventName<T extends string> = `on${Capitalize<T>}`
  2. 聲明合併

    typescript
    // 擴展第三方庫類型
    declare module 'vant' {
      interface ComponentCustomProperties {
        $toast: typeof showToast
      }
    }
  3. 類型體操

    typescript
    // 深度只讀
    type DeepReadonly<T> = {
      readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
    }
    
    // 獲取函數返回類型
    type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
  4. 調試技巧

    typescript
    // 類型調試工具
    type Debug<T> = T extends infer U ? { [K in keyof U]: U[K] } : never
    
    // 編譯時斷言
    type Assert<T extends true> = T
    type Test = Assert<true> // ✅
    // type Test2 = Assert<false> // ❌ 編譯錯誤

📚 相關文檔

通過本指南,你可以在 Vant + Vue 3 項目中充分利用 TypeScript 的類型安全特性,提升開發效率和代碼質量。記住,類型安全不僅僅是為了避免錯誤,更是為了讓代碼更加清晰、可維護和可擴展。

📚 相關內容

想要成為 TypeScript 大師?這些資源助你一臂之力:

通過以上 TypeScript 配置和實踐,你將能夠在 Vant + Vue 3 項目中享受完整的類型安全開發體驗。記住,TypeScript 不僅僅是類型檢查工具,更是提升代碼質量和開發效率的利器!讓我們一起在類型安全的道路上越走越遠!🎯✨

基於Vant構建的企業級移動端解決方案