TypeScript

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

この記事でわかること

Node.js用のtsconfig.jsonを実践解説。strictと追加の型チェック、任意項目、NodeNext、型チェックとビルドの違いを実行できる例で確認します。

tsconfig.json は、型チェックの条件とJavaScriptの生成方法をまとめる設定ファイルです。この記事では小さなNode.js用プロジェクトを作り、strictnoUncheckedIndexedAccessexactOptionalPropertyTypes がそれぞれ何を検査するのか確認します。

対象は関数・配列・オブジェクト型の基礎が分かる方です。掲載コードはTypeScript 4.9.4で検証しています。実際の開発では、利用するNode.jsに対応したTypeScriptと型定義を使ってください。既存のNext.jsなどの設定を、この実習の設定で上書きする必要はありません。

新しいフォルダで準備する

Node.jsとnpm が利用できる環境で、空のフォルダを作成します。@types/node はNode.jsのAPIをTypeScriptに知らせる型定義です。実行するNode.jsのメジャーバージョンと型定義の対応も確認してください。

mkdir tsconfig-practice
cd tsconfig-practice
npm init -y
npm install --save-dev typescript @types/node
npm pkg set type=module
mkdir src

この実習ではpackage.json"type": "module" を設定し、生成したJavaScriptをES Modulesとして実行します。package-lock.json も保存すると、依存関係を再現しやすくなります。

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"],
    "types": ["node"]
  },
  "include": ["src/**/*.ts"]
}

include は最初に読み込むファイルの範囲、rootDir は出力先に入力のディレクトリ構造を反映するときの基準、outDir は出力先です。rootDir 自体には、型チェック対象を選別する働きはありません。また、対象ファイルが参照するファイルは読み込まれるため、include をセキュリティ上の境界として扱わないでください。

lib: ["ES2022"] は標準JavaScriptのAPI、types: ["node"] はNode.jsのグローバルAPIの型を読み込みます。Node.js向けのこの例では、ブラウザの windowdocument を型に含める DOM は不要です。型定義を追加しても、そのAPIが実行環境に実装されるわけではありません。

noEmitOnError は、型エラーがあるときに新たな出力を生成しない設定です。前回のビルドでできた dist 内のファイルは自動削除しません。noEmitOnErrorの公式説明

strict と追加設定の違いを理解する

strict: true は、暗黙的な anynullundefined の扱いなど、複数の厳密な検査をまとめて有効にします。個別の検査は明示的に上書きでき、TypeScriptの更新で検査が強化される場合もあります。strictの公式説明

一方、noUncheckedIndexedAccessexactOptionalPropertyTypes は、strict だけでは有効になりません。この例では配列と任意項目の扱いを確認するため、両方を指定しています。

  • noUncheckedIndexedAccess は、配列の添字アクセスなど、値の存在が保証できない読み取りに undefined の可能性を加えます。
  • exactOptionalPropertyTypes は、任意項目を省略することと、その項目に undefined を代入することを区別します。

空の配列と任意項目を扱う

ファイル: 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 = {};

console.log(firstLabel([])); // 未設定
console.log(firstLabel(["typescript"])); // TYPESCRIPT
console.log(preferences.nickname ?? "ゲスト"); // ゲスト

export {};

空の配列では labels[0]undefined になります。noUncheckedIndexedAccess を有効にしたこの設定では、読み取った値を確認してから toUpperCase() を呼びます。タプルの既知の位置など、存在が型で保証されるアクセスまで一律に undefined になるわけではありません。noUncheckedIndexedAccessの公式説明

nickname?: string は項目を省略できますが、今回の設定では { nickname: undefined } を代入できません。値としても undefined を許すなら nickname?: string | undefined と記述します。任意項目を読み取るときには、項目が存在しない可能性があるので undefined の考慮が必要です。exactOptionalPropertyTypesの公式説明

型チェック・ビルド・実行を分ける

npx tsc -p tsconfig.json --noEmit
npx tsc -p tsconfig.json
node dist/index.js

最初のコマンドは出力を作らず型を確認し、次のコマンドはJavaScriptを生成します。最後のコマンドで「未設定」「TYPESCRIPT」「ゲスト」の順に表示されます。

tsc src/index.ts のように入力ファイルをコマンドへ直接渡すと、tsconfig.json は読み込まれません。設定を使う実習では -p tsconfig.json を指定してください。tsconfig.jsonの公式説明

モジュール設定は実行環境に合わせる

NodeNext はNode.jsのモジュール規則を扱う設定です。この実習のようなES Modulesで自作ファイルを相対パスから読み込む場合、生成後のJavaScriptに合わせて import { helper } from "./helper.js" のように拡張子を指定します。TypeScriptの入力ファイルが helper.ts でも、ここに書くのは出力時の .js です。

バンドラーがコードをまとめるブラウザ向けアプリやフレームワークでは、推奨される設定が異なります。環境が生成した設定を出発点にし、理由を確認して変更してください。モジュール設定の選び方

target は主にJavaScriptの構文水準を決めます。古い環境に不足するAPIを追加する設定ではないため、実際の実行環境でも確認します。

練習問題

  • firstLabel()undefined 判定を外し、型エラーが出ることを確認します。
  • preferences{ nickname: undefined } に変更し、省略との違いを確認します。
  • 元に戻してから型チェックとビルドを行い、正しい出力を再確認します。

型エラーを理由のない as や非nullアサーションで消すと、値が存在しないケースを見逃します。まず欠けた値をどう扱うかを決め、その処理をコードに表してください。

よくある質問

strict を有効にすれば実行時エラーはなくなりますか?

なくなりません。外部データの形式、通信障害、業務ルールなどは、実行時の検証やテストでも確かめます。型チェックは、それらと組み合わせて使う確認手段です。

既存の大きなプロジェクトにはどう導入しますか?

まず出力しない型チェックを行い、影響を把握します。欠けたデータを扱う分岐を追加し、依存するライブラリとの互換性を確認しながら設定を導入します。フレームワークの extends やビルド設定も確認してください。

設定が反映されないときの確認点

読み込んだ設定を確認する

エディターの表示とコマンドの結果が違うときは、実行した場所と対象の設定を確認します。npx tsc -p tsconfig.json --showConfigで最終的な設定を表示し、extendsで継承した項目やincludeも調べます。

型検査と実行環境を別々に確認する

libに型を追加してエラーが消えても、実行環境へ機能が追加されるわけではありません。設定変更後は型チェックに加え、実際のNode.jsやブラウザーで必要な処理を実行してください。

関連記事

TypeScriptとは・学習順・目的別の記事一覧へ戻る

y.
WRITTEN BY

y_ymo10

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

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