tsconfig.json
の設定についてのメモ書きです。
- TypeScript: TSConfig Reference - Docs on every TSConfig option
- TypeScript: Documentation - What is a tsconfig.json
target は 変換後のコードのECMAScriptバージョンを指定する
たとえば、Node.js 14はES2020をサポートしている。そのため、Node.js 14向けのコード(サーバなど)ならtarget: "ES2020"
を指定することで、余計なTranspileが省かれててコードサイズや実行時間が最適化される。
Node.jsのバージョンとベースとなるtarget
の設定は次のパッケージを参照するのが良い。
または、次のページにNode.jsとtsconfig.jsonのtarget
の対応が記載されている。
機能、構文ごとの対応を調べたい場合は次のページを参照する。
Front向けのコードならサポート対象のブラウザがサポートしているECMAScriptのバージョンに合わせる。
📝 誤解しやすいがmoduleは、モジュールをCommonJS, ES Module形式で出力するかなどモジュールの構文のみについてのオプションなので、他のECMAScript構文とは関係ない。webpackやRollupなどの中でTypeScriptを扱っているなら、module: "ESNext"
にしておくことでTree Shakingなどの最適化ができるという仕組み。
target
とmodule
を両方 "ESNext" にする状況は普通はないので、そういった設定は避ける。
tsc --init
で生成された tsconfig.json
のデフォルトONのオプションは使う。
{
"compilerOptions": {
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"strict": true, /* Enable all strict type-checking options. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
targetとmoduleは環境によって違うので、tsconfig/basesを参照する。
strict modeは有効化しないとTypeScriptの恩恵が受けにくい。
後から、strict: true
にするのは、JavaScriptをTypeScriptにするよりもはるかに大変なので、最初からstrict: true
にしたほうが良い。
CommonJSなモジュールをimportするときにinteropするオプション。
- TypeScript の esModuleInterop フラグについて - 30歳からのプログラミング
- Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜
macOSでファイル名が大文字小文字の違いを無視してimportに成功するけど、他のOSでは失敗する現象をコンパイラのレベルで阻止する。ONにしておくのが無難
mochaとjestの型が衝突してコンパイルエラーが起きるのを避けたりするときに使えるオプション。 ライブラリの型の不一致を無視するためのオプションなので、個人的にはオフで困らないならオフの方が健全にみえる。
オフの場合は、yarnのresolusionsなどパッケージ管理ツール側で解決できる。
npmには同等のForce Resolutionsするものがないので、このオプションが必要になったりする状況がある。(acceptDependencies
はどうもforce resolusions的には使えない感じがする)
alias的に使えるが、pathをファイルパスのショートカットとして使うのはツール解析を難しくするので使わない。
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"app/*": ["app/*"],
"config/*": ["app/_config/*"],
"environment/*": ["environments/*"],
"shared/*": ["app/_shared/*"],
"helpers/*": ["helpers/*"],
"tests/*": ["tests/*"]
},
}
このように設定すると import config from "config/path"
と書くと、config
が示すものが一意に決まらなくなる。
config
というモジュールの可能性と paths
の設定での app/_config/path
へのaliasなのかは実際にパスを解決してみないと判別できない。
ツールで解析できなくなることが多いため、ショートカットのために使うのは基本的に避ける。
(リファクタリングの際に問題となっているケースをよくみます)
Next.jsやNuxt.jsのようにディレクトリ名に強い規約がないケースにおいて、
相対パスを避けるために paths
を使うと、逆に{読む、書く}手間を増やしてしまうことがあるため避ける。
解決方法:
- 普通に相対パスで書く。現代的なエディタなら補完が効くので、絶対パスにしたいかどうかは見た目の問題であることがほとんどです
- 名前をちゃんとつけていて、パッケージの境界がきちんとできていれば、実際に
../../../
のようなパスを手書きする機会はかなり少なくなります - 関連: Avoid Export Default - TypeScript Deep Dive 日本語版
- 名前をちゃんとつけていて、パッケージの境界がきちんとできていれば、実際に
- パッケージとして切り出してProject Referencesを使う
- ディレクトリ構造の設計を見直す(関心事でまとめる、テストとアプリケーションを同じディレクトリ以下で扱うなど)
📝 特殊なパターンとして、frontとserver間で型を共有したい場合(どちらもprivateパッケージ)に、型だけのパッケージを作ることを避けるために、paths
で特定のパスだけを参照できるようにするという妥協案で使うことがある。型だけのパッケージはメンテナンスコストがそこまで良いわけではないので、抜け穴的な使い方になるけど現実的なコストとのトレードオフとして理解して使う。この場合も、ハマりやすいのでやっぱりできるだけ paths
の数は少ない状態を維持する。
ちなみに、ライブラリとして公開するパッケージの場合は型だけのパッケージを作った方が参照がきれいになるので、そこにコストをかけたほうが総合的には良い場合が多い。pathsみたいなハックは循環参照とかが生まれやすいので、Project Referencesを使って正攻法で解決するコストを払う。
paths
をパッケージ分離の代替として使うと破綻します。(lerna/npm/yarn workspaceやProject Referencesあたりが適切です)
この paths
は型の参照だけならコンパイルでコケるのでまだいいですが、
スコープを超えた実装の共有を paths
でやると N x M の複雑さを持ち込んでしまうので避けたほうがいい。
BabelやDenoとの相互運用性があがる。
構文的に一部制限が増えるが、いろいろなツールがisolatedModules
を要求してくることがあるので、最初からONしておいたほうが無難かもしれない。
TyepScriptサポート が tsc
じゃなくてbabel
を使っているケースがある。
noFallthroughCasesInSwitch
などのno*
系オプション。
使ってない変数などもコンパイルエラーにできたり、出力コードには影響ないけどLintに近いチェックができる。 個人的にはONにしているけど、使ってない変数が残ってたらコンパイルエラーとかは開発中は少し騒がしい場合もあるので、人の好みによる。 strictオプションに含まれているわけじゃないので、オプショナルなオプション。
/* Additional Checks */
/* Report errors on unused locals. */
"noUnusedLocals": true,
/* Report errors on unused parameters. */
"noUnusedParameters": true,
/* Report error when not all code paths in function return a value. */
"noImplicitReturns": true,
/* Report errors for fallthrough cases in switch statement. */
"noFallthroughCasesInSwitch": true,
/* Include 'undefined' in index signature results */
"noUncheckedIndexedAccess": true,