はじめに
TypeScriptで開発するにあたり、nullとundefinedの扱いは非常に重要になります。
本来、nullやundefinedが混入して欲しくない処理にこれらが混入してしまうと、エラーが発生してしまう上に調査が難航する恐れがあります。そこで、混入してもよい処理と混入してほしくない処理を明示的にコードに落とし込むことができるstrictNullChecksという設定が重要になります。
この記事を読んで身につけられる観点
-
strictNullChecksがnullとundefinedを型のうえでどう扱うか - ON と OFF で、コンパイル時に何が変わり、何が実行時まで残るか
- 値が存在しない可能性を、型と分岐でどう明示するか
- 新規プロジェクトと JS 移行で、ON / OFF をどう判断するか
対象読者
- TypeScript を書き始めたばかりで、
tsconfig.jsonの設定の意味を知りたい人 -
nullやundefinedが原因の実行時エラーを、コンパイルで止めたい人 - JavaScript のコードを TypeScript へ移行している人
- チームで strict 系オプションを ON にするかどうかを判断したい人
strictNullChecks とは
strictNullChecks は、null と undefined を 他の型に勝手に混ぜない ための設定です。
身近な例で言うと、冷蔵庫の「牛乳」ラベルと、中身が存在するかどうかは別問題 というイメージです。
OFF のときは「牛乳」と書いてあれば、中身がある前提でレシピを進めます。
ON のときは「存在しないかもしれない」とラベルに書くか、使う前に確認しなければ先に進めません。
null は「値が存在しない」ことを表す特別な値です。
undefined は「まだ入っていない/省略された」ことを表す特別な値です。
どちらも「値が存在しない」状態に近いですが、ON にすると、この2つを他の型へ入れるには型への明示が必要になります。
strictNullChecks を OFF にするとどうなるのか
strictNullChecks: false のとき、null と undefined は 他の型にも代入できます。
function getLength(text: string) {
return text.length;
}
getLength(null);
実行結果
[ERR]: "Executed JavaScript Failed:"
[ERR]: Cannot read properties of null (reading 'length')
引数の型は string なのに、null を渡してもコンパイルは通ります。
実行すると text.length の時点で止まります。
型を読んでも「値が存在しない可能性」は分かりません。
未来の自分や仲間は、ドキュメントを読まない限り、その可能性に気づけません。
strictNullChecks を ON にするとどうなるのか
strictNullChecks: true にすると、string に null は入りません。
値が存在しない可能性があるなら、型にその旨を書きます。
string | null は「文字列、または null」という意味です。
function getLength(text: string | null) {
return text.length; // Object is possibly 'null'.
}
コンパイラは「text が null かもしれない」と指摘します。
修正は、先に値が存在するかどうかを確認する ことです。
以下のようにif文を用いたチェック処理を入れることで上記のエラーは解消されます。
function getLength(text: string | null) {
if (text === null) {
console.log("nullを検知");
return;
}
console.log(text.length);
}
getLength(null);
getLength("これはnullではありません");
実行結果:
if のあとでは text が string に絞られるため、.length は安全に使えます。
値が存在しない場合の扱いがコード上に残るので、仕様を読まなくても意図が伝わります。
OFF と ON の流れを図にすると、次のようになります。
値が存在するかどうかの確認を人の記憶に頼るのではなく、言語の制約で未チェックを通さないようにします。
この差が、堅牢さ(壊しにくいこと)につながります。
OFF と ON の違い
| 観点 | strictNullChecks: false |
strictNullChecks: true |
|---|---|---|
null / undefined の代入 |
他の型にも代入できる | 型に書いたときだけ入る |
| 値が存在しない可能性 | 型に現れない |
string | null のように型に現れる |
| 未チェックのアクセス | コンパイルは通る | コンパイルエラー |
| 向いている場面 | 移行コストなど、明確な理由がある場合の一時的な運用 | 原則はこちら。新規・既存移行を問わず基本的に推奨 |
基本は true で良さそうかなと考えています。既存コードの移行コストなど、明確な理由がある場合に限り、一時的に false を検討します。
使用するメリット
-
nullやundefinedの未チェックによる一部の実行時エラーを、コンパイルの時点で検出できる - 「この値は存在しない可能性がある」が型に残るため、ドキュメントを読まなくても意図が伝わる
- リファクタリングで戻り値に値が存在しない可能性が混ざったとき、呼び出し側の未チェックがコンパイラに検出される
チーム開発では、レビューで「null チェックは書いたか」と確認し続けるより、仕組みで誤用を防ぐほうが責務が安定します。
使用するデメリット
ON にすると、値が存在しない可能性がある箇所で union 型(string | null のような「A または B」)と、if による絞り込みを覚える必要があります。
既存コードに入れると、エラーが一度に大量に出ることもあります。
学習コストは上がります。
ただしそのエラーの多くは、もともと実行時まで隠れていた危険箇所です。
コストの中身は、新しい文法の暗記よりも、「nullやundefinedの可能性を型で明示する習慣」へ移すことにあります。
tsconfig.json の設定例
{
"compilerOptions": {
"strict": true
}
}
strict: true には strictNullChecks が含まれます。
移行コストなど、明確な理由があって一時的に OFF にする場合は、次のように書きます。
{
"compilerOptions": {
"strict": true,
"strictNullChecks": false
}
}
アンチパターン
-
エラーを消すために
!を付ける —text!.lengthは「値は存在する」とコンパイラへ宣言するだけで、実行時の安全性は変わりません -
as stringで値が存在しない可能性を無視する —asはコンパイラへの「この型だと思って」という宣言であり、実行時の値はnullのままになりえます -
明確な意図を持たずに
strictNullChecksをfalseにする — 明確にこの意図を持って設定を切るという判断をせずに「なんとなく」や「コンパイルエラーが減る」という見た目上の判断で切ると後になって困ることになります - 「チェックを忘れない」という運用で済ませる — 人が増えるほど漏れるため、言語の制約で未チェックを通さないほうが長期保守では安定します
まとめ
strictNullChecks は、値が存在しない可能性を他の型に混ぜない ための設定です。
OFF だと TypeScript は「型があるように見えて、値が存在するかどうかのチェックはしていない」状態になります。
値が存在しない可能性があるなら型に書き、使う前に確認します。
この書き方が、実行時エラーの混入を防ぎ、未来の自分と仲間に意図を伝えます。
新規プロジェクトでは ON を前提とします。
既存の JavaScript を TypeScript へ移行する場合も、基本は ON を維持します。
ただし、移行コストなど明確な理由がある場合は、一時的に OFF にすることも検討できます。
その場合も、最終的には ON に戻すことを目標とします。
この判断軸が、Effective TypeScript から私が学んだことです。
参考文献
- Effective TypeScript 第2版 ―型システムの力を最大限に引き出す83項目(著:Dan Vanderkam 訳:今村 謙士)


