直接跳到內容

搭配 TypeScript 使用 Vue

像 TypeScript 這樣的類型系統可以在編譯時通過靜態分析檢測出很多常見錯誤。這減少了生產環境中的運行時錯誤,也讓我們在重構大型項目的時候更有信心。通過 IDE 中基於類型的自動補全,TypeScript 還改善了開發體驗和效率。

Vue 本身就是用 TypeScript 編寫的,並對 TypeScript 提供了良好的支持。所有的 Vue 官方庫都自帶了類型聲明文件,開箱即用。

項目配置

create-vue,即官方的項目腳手架工具,提供了搭建基於 Vite 且 TypeScript 就緒的 Vue 項目的選項。

總覽

在基於 Vite 的配置中,開發服務器和打包器將只會對 TypeScript 文件執行語法轉譯,而不會執行任何類型檢查,這保證了 Vite 開發服務器在使用 TypeScript 時也能始終保持飛快的速度。

  • 在開發階段,我們推薦你依賴一個好的 IDE 配置來獲取即時的類型錯誤反饋。

  • 對於單文件組件,你可以使用工具 vue-tsc 在命令行檢查類型和生成類型聲明文件。vue-tsc 是對 TypeScript 自身命令行界面 tsc 的一個封裝。它的工作方式基本和 tsc 一致。除了 TypeScript 文件,它還支持 Vue 的單文件組件。你可以在開啟 Vite 開發服務器的同時以偵聽模式運行 vue-tsc,或是使用 vite-plugin-checker 這樣在另一個 worker 線程裡做靜態檢查的插件。

  • Vue CLI 也提供了對 TypeScript 的支持,但是已經不推薦了。詳見下方的說明

IDE 支持

  • 強烈推薦 Visual Studio Code (VSCode),因為它對 TypeScript 有著很好的內置支持。

    • Vue - Official (之前是 Volar) 是官方的 VSCode 擴展,提供了 Vue 單文件組件中的 TypeScript 支持,還伴隨著一些其他非常棒的特性。

      TIP

      Vue - Official 擴展取代了我們之前為 Vue 2 提供的官方 VSCode 擴展 Vetur。如果你之前已經安裝了 Vetur,請確保在 Vue 3 的項目中禁用它。

  • WebStorm 對 TypeScript 和 Vue 也都提供了開箱即用的支持。其他的 JetBrains IDE 也同樣可以通過一個免費插件支持。從 2023.2 版開始,WebStorm 和 Vue 插件內置了對 Vue 語言服務器的支持。你可以在設置 > 語言和框架 > TypeScript > Vue 下將 Vue 服務設置為在所有 TypeScript 版本上使用 Volar 集成。默認情況下,Volar 將用於 TypeScript 5.0 及更高版本。

配置 tsconfig.json

通過 create-vue 搭建的項目包含了預先配置好的 tsconfig.json。其底層配置抽象於 @vue/tsconfig 包中。在項目內我們使用 Project References 來確保運行在不同環境下的代碼的類型正確 (例如應用代碼和測試代碼應該有不同的全局變量)。

手動配置 tsconfig.json 時,請留意以下選項:

參考:

關於 Vue CLI 和 ts-loader

像 Vue CLI 這樣的基於 webpack 搭建的項目,通常是在模塊編譯的過程中順便執行類型檢查,例如使用 ts-loader。然而這並不是一個理想的解決方案,因為類型系統需要了解整個模塊關係才能執行類型檢查。loader 中只適合單個模塊的編譯,並不適合做需要全局信息的工作。這導致了下面的問題:

  • ts-loader 只能對在它之前的 loader 編譯轉換後的代碼執行類型檢查,這和我們在 IDE 或 vue-tsc 中看到的基於源代碼的錯誤提示並不一致。

  • 類型檢查可能會很慢。當它和代碼轉換在相同的線程/進程中執行時,它會顯著影響整個應用的構建速度。

  • 我們已經在 IDE 中通過單獨的進程運行著類型檢查了,卻還要在構建流程中執行類型檢查導致降低開發體驗,這似乎不太合理。

如果你正通過 Vue CLI 使用 Vue 3 和 TypeScript,我們強烈建議你遷移到 Vite。我們也在為 CLI 開發僅執行 TS 語法轉譯的選項,以允許你切換至 vue-tsc 來執行類型檢查。

常見使用說明

defineComponent()

為了讓 TypeScript 正確地推導出組件選項內的類型,我們需要通過 defineComponent() 這個全局 API 來定義組件:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  // 啟用了類型推導
  props: {
    name: String,
    msg: { type: String, required: true }
  },
  data() {
    return {
      count: 1
    }
  },
  mounted() {
    this.name // 類型:string | undefined
    this.msg // 類型:string
    this.count // 類型:number
  }
})

當沒有結合 <script setup> 使用組合式 API 時,defineComponent() 也支持對傳遞給 setup() 的 prop 的推導:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  // 啟用了類型推導
  props: {
    message: String
  },
  setup(props) {
    props.message // 類型:string | undefined
  }
})

參考:

TIP

defineComponent() 也支持對原生 JavaScript 編寫的組件進行類型推導。

在單文件組件中的用法

要在單文件組件中使用 TypeScript,需要在 <script> 標籤上加上 lang="ts" 的屬性。當 lang="ts" 存在時,所有的模板內表達式都將受到更嚴格的類型檢查。

vue
<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      count: 1
    }
  }
})
</script>

<template>
  <!-- 啟用了類型檢查和自動補全 -->
  {{ count.toFixed(2) }}
</template>

lang="ts" 也可以用於 <script setup>

vue
<script setup lang="ts">
// 啟用了 TypeScript
import { ref } from 'vue'

const count = ref(1)
</script>

<template>
  <!-- 啟用了類型檢查和自動補全 -->
  {{ count.toFixed(2) }}
</template>

模板中的 TypeScript

在使用了 <script lang="ts"><script setup lang="ts"> 後,<template> 在綁定表達式中也支持 TypeScript。這對需要在模板表達式中執行類型轉換的情況下非常有用。

這裡有一個假想的例子:

vue
<script setup lang="ts">
let x: string | number = 1
</script>

<template>
  <!-- 出錯,因為 x 可能是字符串 -->
  {{ x.toFixed(2) }}
</template>

可以使用內聯類型強制轉換解決此問題:

vue
<script setup lang="ts">
let x: string | number = 1
</script>

<template>
  {{ (x as number).toFixed(2) }}
</template>

TIP

如果正在使用 Vue CLI 或基於 webpack 的配置,支持模板內表達式的 TypeScript 需要 vue-loader@^16.8.0

使用 TSX

Vue 也支持使用 JSX / TSX 編寫組件。詳情請查閱渲染函數 & JSX

泛型組件

泛型組件支持兩種使用方式:

特定 API 的使用指南

搭配 TypeScript 使用 Vue已經加載完畢