TL;DR

  • TypeScript のパフォーマンスの問題には、ビルド(tsc)のものと言語サービス(tsserver)のものがある。
  • ビルド時の型チェックを省く、不要なコードやパッケージを消す、インクリメンタルビルドとプロジェクト参照を使う、重い型を見直す、の 4 つで改善できる。

内容

ビルド時に型チェックをしない

TypeScript は、バンドラ(ts-loader を使った webpack や Vite)や ts-node など、他のツールを経由して実行されることが多い。このうち、Vite はデフォルトでトランスパイルだけを行うが、webpack(ts-loader)や ts-node は型チェックも行う。これを無効にするにはオプションを指定する(ts-node、ts-loader)。

デッドコードや不要なパッケージを削除する

noUnusedLocals を有効にすると、使われていないコードや型を検出できる。ただし、export されているのに使われていないシンボルは検出できないので、Knip のようなツールが必要になる。Knip は使っていないパッケージも検出できる。

プロジェクトに含まれるファイルの量を確かめるには、次のコマンドが使える。1 つ目はプロジェクトに含まれるすべてのソースを表示し、2 つ目はコンパイルされる各ファイルのバイト数を可視化する。

npx tsc --listFiles
npx tsc --noEmit --listFiles | xargs stat -f "%z %N" | npx webtreemap-cli

インクリメンタルビルドとプロジェクト参照を使う

incremental を有効にすると、初回のビルドで作業の一部を .tsbuildinfo ファイルに書き出す。2 回目以降のビルドではこのファイルを読み込むので、速くビルドできる。プロジェクト参照と組み合わせると、さらに最適化できる。

型を見直す

型の書き方もコンパイラの速さに影響する。

  • 大きなユニオン型を避ける。たとえば次の Year は 1000 個のメンバーを持つ

    type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
    type Year = `2${Digit}${Digit}${Digit}`
  • type のインターセクション(&)ではなく、interface の extends を使う(項目13)

  • 戻り値に型アノテーションを付ける(推論できる型を書きすぎないことについては項目18)

  • 末尾再帰でない再帰的なジェネリック型を避ける(項目57)

キーワード