TypeScript で Vue を使用する
TypeScript のような型システムは、ビルド時に静的解析で多くの一般的なエラーを検出できます。これにより、本番環境でのランタイムエラーの確率を下げ、より自信を持って大規模アプリケーションのコードをリファクタリングできるようになります。TypeScript は、IDE における型ベースの自動補完により、開発者の人間工学も改善します。
Vue 自体が TypeScript で書かれており、第一級の TypeScript のサポートを提供します。全ての公式 Vue パッケージには型定義が付属されているため、すぐに利用することができます。
プロジェクトのセットアップ
create-vue
は公式のプロジェクトセットアップツールで、Vite を用いた TypeScript 対応の Vue プロジェクトをセットアップするためのオプションを提供します。
概要
Vite ベースのセットアップでは、開発サーバーとそのバンドラーはトランスパイルのみ行い、型チェックは行いません。これにより、Vite の開発サーバーは TypeScript を使用していても超高速に動作し続けることが保証されます。
開発中は、すぐに型エラーのフィードバックを得るために、優れた IDE セットアップ に頼ることを推奨します。
SFC を使用する場合、コマンドラインでの型チェックと型宣言の生成には
vue-tsc
を使用します。vue-tsc
は TypeScript 独自のコマンドラインインターフェースであるtsc
のラッパーです。TypeScript のファイルに加えて Vue SFC をサポートする以外は、tsc
とほぼ同じように動作します。Vite の開発サーバーと並行して、vue-tsc
をウォッチモードで実行するか、別のワーカースレッドでチェックを実行する vite-plugin-checker のような Vite プラグインを使用できます。Vue CLI は TypeScript もサポートしていますが、推奨されなくなりました。以下の注意を参照してください。
IDE のサポート
Visual Studio Code (VSCode) は、すぐに利用できる TypeScript サポートを提供しているため、強く推奨されます。
Volar は公式 VSCode 拡張機能です。Vue SFC 内部での TypeScript のサポートなど、その他の優れた機能を持ちます。
TIP
Volar は Vue 2 の公式 VSCode 拡張機能である Vetur を置き換えるものです。現在 Vetur がインストールされている場合、Vue 3 のプロジェクトで必ず無効化してください。
TS ファイル内で
*.vue
をインポートする際に型サポートを得る場合、TypeScript Vue Plugin も必要となります。
WebStorm は TypeScript と Vue の両方をすぐに利用できるようサポートしています。他の JetBrains IDE も最初から、または この無料プラグイン で、これらをサポートしています。
tsconfig.json
の構成
create-vue
を使ってセットアップしたプロジェクトには、構成済みの tsconfig.json
が含まれます。ベースとなった構成は @vue/tsconfig
パッケージにて抽象化されています。プロジェクト内部では、Project References を使って、異なる環境(例: アプリコードとテストコードで異なるグローバル変数を持つ必要がある)で実行されるコードに対して正しい型を保証しています。
tsconfig.json
を手動で構成する場合、いくつかの注目すべきオプションは以下の通りです:
Vite は TypeScript のトランスパイルに esbuild を使用しており、単一ファイルのトランスパイル制限に従うので、
compilerOptions.isolatedModules
をtrue
に設定してください。Options API を使用する場合は、コンポーネントオプションにおける this の型チェックを活用するために
compilerOptions.strict
をtrue
に設定 (もしくは少なくとも strict フラグの一部であるcompilerOptions.noImplicitThis
をtrue
に設定) する必要があります。そうでないと、this は常に any 型として扱われます。例えば
create-vue
プロジェクトでデフォルトで設定される@/*
エイリアスのような resolver エイリアスをビルドツールで設定した場合、TypeScript 用の設定として、compilerOptions.paths
にもエイリアスを設定する必要があります。
参照:
Volar Takeover Mode
このセクションは、VSCode + Volar の場合のみ該当します。
Vue SFC と TypeScript を連携させるために、 Volar は Vue 固有のサポートをパッチした別の TS 言語サービスインスタンスを作成し、Vue SFC 内で使われます。同時に、プレーンな TS ファイルは依然として VSCode の組み込み TS 言語サービスによって処理されます。TS ファイル内での Vue SFC インポートのサポート時に TypeScript Vue Plugin が必要なのはそういった理由からです。このデフォルトのセットアップ方法はうまく動きますが、プロジェクトごとに 2 つの TS 言語サービスインスタンスが実行されます: 1 つは Volar の言語サービス、もう 1 つは VSCode の組み込み言語サービスです。これは少し非効率であり、大規模プロジェクトではパフォーマンスの問題を引き起こす可能性があります。
Volar はパフォーマンスの改善のために、"Takeover Mode" と呼ばれる機能を提供しています。Takeover Mode では、Volar は単一の TS 言語サービスインスタンスを使用して Vue と TS の両ファイルをサポートします。
Takeover Mode を有効にするには以下の手順に従って、VSCode の組み込み TS 言語サービスをプロジェクトのワークスペースのみで無効化する必要があります:
- プロジェクトのワークスペース内で、
Ctrl + Shift + P
(macOS:Cmd + Shift + P
)でコマンドパレットを表示します。 built
と入力し、"Extensions: Show Built-in Extensions" を選択します。- 拡張機能の検索ボックスに
typescript
と入力します(@builtin
というプレフィックスは削除しないでください)。 - "TypeScript and JavaScript Language Features" の小さい歯車のアイコンをクリックし、"Disable (Workspace)" を選択してください。
- ワークスペースを再読み込みしてください。Vue または TS ファイルを開くと、Takeover Mode が有効になります。
Vue CLI と ts-loader
に関する注意
Vue CLI など、webpack ベースのセットアップでは、モジュールの変換パイプラインの一部で、例えば ts-loader
を使って型チェックを行うことが一般的です。しかし、これは型チェックを行うためにモジュールグラフ全体の知識が必要となるので、クリーンな解決法とは言えません。個々のモジュールの変換ステップは、単にこのタスクを行うための良い方法ではないのです。これは、以下のような問題を引き起こします:
ts-loader
は変換後のコードしか型チェックができません。これは、IDE やvue-tsc
でみられる、ソースコードに直接マップされるエラーとは一致しません。型チェックは遅くなる可能性があります。コード変換と同じスレッド / プロセスで行われると、アプリケーション全体のビルド速度に大きく影響します。
すでに IDE で型チェックを別のプロセスで実行しているので、開発体験を低下させるコストは、単純に適切なトレードオフではありません。
現在、Vue CLI 経由で Vue 3 + TypeScript を使っている場合、Vite への移行を強く推奨します。トランスパイルのみでの TS サポートを有効にする CLI オプションにも取り組んでいるので、型チェックのために vue-tsc
に切り替えることができます。
一般的な使用上の注意
defineComponent()
TypeScript がコンポーネントオプション内の型を適切に推論できるようにするには、defineComponent()
を使ってコンポーネントを定義する必要があります:
ts
import { defineComponent } from 'vue'
export default defineComponent({
// 型推論が有効
props: {
name: String,
msg: { type: String, required: true }
},
data() {
return {
count: 1
}
},
mounted() {
this.name // type: string | undefined
this.msg // type: string
this.count // type: number
}
})
<script setup>
を使わずに Composition API を使用する場合、defineComponent()
は setup()
に渡されたプロパティを推論することができます:
ts
import { defineComponent } from 'vue'
export default defineComponent({
// 型推論が有効
props: {
message: String
},
setup(props) {
props.message // type: string | undefined
}
})
参照:
TIP
defineComponent()
は、プレーンな JavaScript で定義されたコンポーネントに対しても型推論を行うことができます。
単一ファイルコンポーネントでの使用法
SFC で 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
<template>
は、<script lang="ts">
や <script setup lang="ts">
が使われている場合、バインディング式について、TypeScript をサポートします。これは、テンプレート内の式で型キャストを行う必要がある場合に便利です。
不自然な例を以下に示します:
vue
<script setup lang="ts">
let x: string | number = 1
</script>
<template>
<!-- x は string の可能性があるためエラー -->
{{ 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
が必要になります。