TypeScriptのtsconfig.json実践入門:strictと追加の型チェックを設定する
小さなNode.jsプロジェクトでtsconfig.jsonを設定。strict、noUncheckedIndexedAccess、exactOptionalPropertyTypesの違い、型チェックとビルド、環境に合うmodule設定を解説します。
「コンパイルは通るのに、空の配列から取り出した値でエラーになる」。このような問題を開発中に見つけやすくするには、TypeScriptの設定を理解することが役立ちます。既存のコンパイル入門から一歩進み、この記事では小さなNode.js用プロジェクトで型チェックを強めます。アプリを置き換えるための万能設定ではなく、それぞれの設定の役割を確かめる実習です。
【1. 新しいフォルダで準備する】
保守中のNode.jsとnpmを用意し、空のフォルダで次のコマンドを実行します。TypeScriptはプロジェクトの開発用依存関係として追加します。既存プロジェクトに適用する場合は、フレームワークが用意する設定やextendsの関係を先に確認してください。mkdir tsconfig-practicecd tsconfig-practicenpm init -ynpm install --save-dev typescriptnpm pkg set type=modulemkdir src
package.json に "type": "module" が設定されます。この実習ではNode.jsのES Modulesとして実行します。インストール後のpackage-lock.jsonも保存しておくと、後から依存関係を再現しやすくなります。
【2. tsconfig.jsonを配置する】
ファイル: tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noEmitOnError": true,
"rootDir": "src",
"outDir": "dist",
"lib": [
"ES2022",
"DOM"
],
"types": []
},
"include": [
"src/**/*.ts"
]
}
include はsrc以下のTypeScriptファイルを対象にし、rootDir と outDir は入力と生成先の構成を決めます。noEmitOnError は型エラーがあるビルドで新たな出力を生成しない設定です。過去に作られたdist内のファイルを自動削除する設定ではありません。
この例は標準のconsoleだけを使うため、型定義の lib にDOMを含め、types: [] で自動読み込みする@typesパッケージを限定しています。DOMを指定してもNode.jsにwindowやdocumentが追加されるわけではありません。Node固有のAPIを使う実プロジェクトでは、環境に合う @types/node を追加し、types: ["node"]、lib: ["ES2022"] などに調整します。
【3. strictが有効にする検査と、追加設定の違い】
strict: true は、暗黙的なanyやnull・undefinedの扱いなど、複数の厳密な検査を有効にします。個々の項目は明示的に上書きでき、将来のバージョンで検査が強化される場合もあります。詳細は strictの公式説明(https://www.typescriptlang.org/tsconfig/strict.html) を参照してください。
一方、noUncheckedIndexedAccess と exactOptionalPropertyTypes はstrictだけでは有効になりません。この実習では、配列や任意項目の扱いを確認するため、両方を明示的に有効にしています。
【4. 空の配列と任意項目を安全に扱う】
ファイル: src/index.ts
type Preferences = { nickname?: string };
function firstLabel(labels: string[]): string {
const first = labels[0];
if (first === undefined) {
return "未設定";
}
return first.toUpperCase();
}
const preferences: Preferences = {}; // nicknameは省略できる
console.log(firstLabel([])); // 未設定
console.log(firstLabel(["typescript"])); // TYPESCRIPT
console.log(preferences.nickname ?? "ゲスト"); // ゲスト
labels[0] は、配列が空ならundefinedになります。noUncheckedIndexedAccess を有効にすると、この可能性を型に反映するため、例ではundefinedを判定してからtoUpperCaseを呼んでいます。添字アクセスの詳しい扱いは 公式の設定説明(https://www.typescriptlang.org/tsconfig/noUncheckedIndexedAccess.html) を参照してください。
nickname?: string は項目自体を省略できることを表します。exactOptionalPropertyTypes を有効にした状態で { nickname: undefined } を代入すると型エラーになります。undefinedを値として渡す設計なら nickname?: string | undefined と明示します。この区別は、項目の存在を検査する処理などで重要です。公式の説明(https://www.typescriptlang.org/tsconfig/exactOptionalPropertyTypes.html) も参照してください。
【5. 型チェック・ビルド・実行を分ける】npx tsc -p tsconfig.json --noEmitnpx tsc -p tsconfig.jsonnode dist/index.js
1行目は出力を作らず型だけを確認し、2行目はJavaScriptを生成します。3行目で実行すると「未設定」「TYPESCRIPT」「ゲスト」と表示されます。tsc src/index.ts のように入力ファイルを直接指定する実行は、プロジェクト設定を使う -p の実行とは扱いが異なります。この実習では、設定を確実に指定するため -p tsconfig.json を使います。
【6. module設定は実行環境に合わせる】
この実習の NodeNext はNode.jsのモジュールの扱いに合わせる設定です。ES Modulesで別の自作ファイルを相対importする場合、生成後の実行に合わせて ./helper.js のような拡張子が必要になる場面があります。Viteなどのバンドラーを使うブラウザ向けアプリでは、生成された設定を出発点にしてください。設定を選ぶ基準は 公式のChoosing Compiler Options(https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html) にまとまっています。
target は主に出力するJavaScriptの構文水準を指定します。古い環境に不足するAPIを自動で追加する設定ではないため、実際に動かすNode.jsやブラウザでの確認も必要です。
【練習問題と段階的な導入】
firstLabel内のundefined判定を一度外して、コンパイラが問題を指摘するか確認しましょう。次にpreferencesを { nickname: undefined } に変更し、任意項目の設定の働きを確認します。両方を元に戻してから、もう一度ビルドと実行を行ってください。
既存の大きなプロジェクトでは、一度に設定を変えると多くのエラーが出る場合があります。まず型チェックだけを実行して影響を把握し、データが欠ける場合の処理を追加します。理由を確認せずにasや非nullアサーションでエラーを消すと、今回見つけたい問題が再び隠れてしまいます。
【よくある質問】
【strictを有効にすれば実行時エラーはなくなりますか?】
なくなりません。外部入力の内容、通信障害、業務ルールの間違いなどは、実行時の検証やテストも必要です。型チェックは、それらと組み合わせる確認手段です。
【参考資料】
・TypeScript: What is a tsconfig.json(https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)
・TSConfig: strict(https://www.typescriptlang.org/tsconfig/strict.html)
・TSConfig: noUncheckedIndexedAccess(https://www.typescriptlang.org/tsconfig/noUncheckedIndexedAccess.html)
・TSConfig: exactOptionalPropertyTypes(https://www.typescriptlang.org/tsconfig/exactOptionalPropertyTypes.html)
・TypeScript: Choosing Compiler Options(https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html)