strict モードとは、tsconfig.json の "strict": true オプションのことです。暗黙の any、チェックされない null と undefined、安全でない関数の代入、初期化されないクラスのフィールドを検出する8つの型チェックのフラグを、ひとつのスイッチでまとめて切り替えます。TypeScript 7 では strict はデフォルトで有効です。strict を通るコードは次のようになります:
出力は no user、SyntaxError、42 です。: number、?.、instanceof のチェックのどれかを取り除くと、ファイルはコンパイルできなくなります。
有効にする
{
"compilerOptions": {
"strict": true
}
}
個々のフラグは、どちらの方向でも strict より優先されます。"strict": true, "strictNullChecks": false は null チェック以外をすべて有効にし、"strict": false, "noImplicitAny": true はそのひとつだけを有効にします。新しい strict 系のフラグはこのグループに加わるので、strict を使えば今後のリリースで追加されるチェックも有効になります。
TypeScriptの strict は、実行時のモードであるJavaScriptの "use strict" ディレクティブとは関係ありません。TypeScript 7 は必要な場所に常に "use strict" を出力し、alwaysStrict: false を設定するとエラー TS5108 になります(このオプションは削除されました)。
各フラグが検出するもの
| フラグ | 報告する内容 | エラー |
|---|---|---|
noImplicitAny | 型が気づかないうちに any になるパラメーターや変数 | TS7006 Parameter 'x' implicitly has an 'any' type. |
strictNullChecks | null や undefined かもしれない値の使用 | TS18048 'u' is possibly 'undefined'. |
strictFunctionTypes | パラメーターの型が必要な型より狭い関数の代入 | TS2322 |
strictBindCallApply | .call、.bind、.apply への誤った引数 | TS2345 |
strictPropertyInitialization | 一度も代入されないクラスのフィールド | TS2564 Property 'name' has no initializer and is not definitely assigned in the constructor. |
noImplicitThis | 入れ子の function の中などで暗黙の any 型になる this | TS2683 |
useUnknownInCatchVariables | 絞り込む前の catch 変数(型は unknown)の使用 | TS18046 'err' is of type 'unknown'. |
strictBuiltinIteratorReturn | 組み込みのイテレーターの it.next().value を常に定義済みとして扱うこと | TS2322 |
いちばん多くのコードに影響するのは noImplicitAny と strictNullChecks の2つです。次のブロックはわざと両方に違反しています:
出力は index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type. と index.ts(11,13): error TS18048: 'user' is possibly 'undefined'. です。strict がなければこれはコンパイルが通り、実行時に TypeError: Cannot read properties of undefined (reading 'name') でクラッシュします。strict があれば、そのクラッシュがコンパイルエラーになります。直し方はこのページの最初のブロックです。
strictFunctionTypes が存在する理由
文字列しか扱えない関数を、数値が来るかもしれない場所で使ってはいけません。次のチェックは @ts-expect-error で黙らせてあるので、実行してエラーが何を防いでいるかを確認できます:
コメントがなければ、この代入はエラー TS2322 Type '(s: string) => void' is not assignable to type 'Handler'. になり、続いて Types of parameters 's' and 'value' are incompatible. と表示されます。設計上の例外がひとつ残っています。メソッド構文で宣言したメソッド(インターフェースの中の handle(value: string | number): void)のパラメーターは今もゆるい方法でチェックされるので、そうしたメソッドには shout をエラーなしで代入できてしまいます。
strictPropertyInitialization
クラスのフィールドは、宣言かコンストラクターで必ず値を受け取らなければなりません。満たす方法は3つあります:
出力:
Account {
owner: 'Ada',
balance: 0,
history: [],
lastLogin: undefined,
sessionId: 's-1'
}
target が ES2022 ではクラスのフィールドは本物のJavaScriptのフィールドなので、lastLogin と sessionId のプロパティは値 undefined で存在します。! はチェックを取り除くだけで実行時の保護は何も加えないので、ほかの書き方を優先しましょう。このフラグには strictNullChecks が必要で、それを無効にするとこちらも無効になります。
既存のプロジェクトで strict を有効にする
大きなコードベースを一度に strict に切り替えると、何百ものエラーが出ることがあります。strict は TypeScript 7 のデフォルトなので、tsconfig.json で一度も strict に触れていないプロジェクトをアップグレードすると、自動的に有効になります。移行中に以前の動作が必要なら "strict": false と書きます。ビルドを通したまま進める手順:
"strict": trueを追加し、エラーがいちばん多いフラグ(たいてい"strictNullChecks": falseと"noImplicitAny": false)を無効にします。- 残りのエラーを直したら、フラグをもうひとつ有効にして繰り返します。
noImplicitAnyの修正はほとんどがパラメーターの型注釈です。strictNullChecksでは、値がないかもしれない場所に| undefinedを追加し、?.、??、ifのチェックで処理します。- 修正を後回しにするしかない行には、理由を添えて
// @ts-expect-errorを書きます。@ts-ignoreと違い、問題がなくなるとエラーを報告するので、リストは自然に減っていきます。
as any や ! でエラーをまとめて黙らせるのはやめましょう。どれも、そのフラグが見つけるために追加されたバグそのものを隠してしまいます。
strict に含まれない便利なフラグ
これらは、正しいことも多いコードを拒否するので別になっています。それでも多くのプロジェクトが有効にしています。
| フラグ | 内容 |
|---|---|
noUncheckedIndexedAccess | arr[i] と record[key] の型に undefined が含まれる |
exactOptionalPropertyTypes | debug?: boolean はキーの省略は受け付けるが debug: undefined は受け付けない |
noImplicitReturns | 戻り値のある関数のすべてのコードパスが return しなければならない(TS7030) |
noImplicitOverride | 基底クラスのメソッドをオーバーライドするメソッドには override が必要(TS4114) |
noFallthroughCasesInSwitch | 空でない case は break、return、throw で終わらなければならない(TS7029) |
noUnusedLocals, noUnusedParameters | 使われていない変数とパラメーターがエラーになる |
noPropertyAccessFromIndexSignature | インデックスシグネチャのキーには obj.key ではなく obj["key"] を使わなければならない |
実際のバグをいちばん多く見つけるのは noUncheckedIndexedAccess です。有効にすると:
const scores = [90, 85];
const d: Record<string, number> = {};
const third: number = scores[2]; // error TS2322: Type 'number | undefined' is not assignable to type 'number'.
const c: number = d["x"]; // same error
const safe = scores[2] ?? 0; // number
for (const s of scores) { // for...of is not affected: s is number
console.log(s);
}
TypeScript 7 の tsc --init は、生成する設定で strict と並んで noUncheckedIndexedAccess と exactOptionalPropertyTypes を有効にします。
よくある質問
TypeScriptの strict モードは何をしますか?
"strict": true は、より厳しい一連のチェックを有効にします: noImplicitAny、strictNullChecks、strictFunctionTypes、strictBindCallApply、strictPropertyInitialization、noImplicitThis、useUnknownInCatchVariables、strictBuiltinIteratorReturn。これらを合わせると、any が気づかないうちに紛れ込むのを防ぎ、null と undefined を型システムの一部にします。
TypeScriptの strict モードはデフォルトで有効ですか?
TypeScript 7 では有効です。strict のデフォルトは true なので、strict の設定がないプロジェクトではすべての strict チェックが有効になります。tsc --init も "strict": true を明示的に書き込みます。無効にするには "strict": false と書く必要があります。
strict のチェックのひとつだけを無効にして、残りを有効のままにできますか?
できます。個々のフラグは strict より優先されます: { "strict": true, "strictNullChecks": false } は、null チェック以外のすべての strict チェックを有効のままにします。大きなコードベースを1フラグずつ移行するときの一般的な方法です。
TypeScriptの strict モードはJavaScriptの "use strict" と同じですか?
違います。"use strict" はコードの動作を変えるJavaScriptの実行時のモードです。TypeScriptの strict は型チェッカーが報告する内容を変えるだけです。TypeScript 7 はモジュールでない出力には常に "use strict" を出力し、alwaysStrict: false は削除されたオプションです。
strict に noUncheckedIndexedAccess は含まれますか?
含まれません。noUncheckedIndexedAccess、exactOptionalPropertyTypes、noImplicitReturns、noImplicitOverride、noFallthroughCasesInSwitch は、自分で有効にする別のフラグです。tsc --init は生成する設定で最初の2つを有効にします。