TypeScript

TypeScriptのtsconfig.json実践入門:strictと追加の型チェックを設定する

この記事でわかること

小さなNode.jsプロジェクトでtsconfig.jsonを設定。strict、noUncheckedIndexedAccess、exactOptionalPropertyTypesの違い、型チェックとビルド、環境に合うmodule設定を解説します。

「コンパイルは通るのに、空の配列から取り出した値でエラーになる」。このような問題を開発中に見つけやすくするには、TypeScriptの設定を理解することが役立ちます。既存のコンパイル入門から一歩進み、この記事では小さなNode.js用プロジェクトで型チェックを強めます。アプリを置き換えるための万能設定ではなく、それぞれの設定の役割を確かめる実習です。

【1. 新しいフォルダで準備する】

保守中のNode.jsとnpmを用意し、空のフォルダで次のコマンドを実行します。TypeScriptはプロジェクトの開発用依存関係として追加します。既存プロジェクトに適用する場合は、フレームワークが用意する設定やextendsの関係を先に確認してください。

mkdir tsconfig-practice
cd tsconfig-practice
npm init -y
npm install --save-dev typescript
npm pkg set type=module
mkdir 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) を参照してください。

一方、noUncheckedIndexedAccessexactOptionalPropertyTypes は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 --noEmit
npx tsc -p tsconfig.json
node 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)

y.
WRITTEN BY

y_ymo10

SEの部屋で、TypeScript・Next.jsの開発ノートを公開しています。

ほかの開発ノートを読む →