strictNullChecks が有効なら(strict に含まれています)、null と undefined はそれぞれ独立した型です。string が null になることはなく、欠けているかもしれない値は string | null のように型でそれを示す必要があり、TypeScriptは値を使う前にその場合を処理するよう求めます。
チェックのあとでは name が string だとコンパイラーがわかるので、.split が使えます。このページの残りでは、そのチェックの方法と、それを短く書ける演算子を説明します。
strictNullChecks と「possibly null」エラー
型が null や undefined を含むとき、TypeScriptは値が常に存在するかのように使うことを拒否します:
index.ts(3,22): error TS18047: 'name' is possibly 'null'.
undefined 版は TS18048 'x' is possibly 'undefined' です。strictNullChecks がなければ、null と undefined はあらゆる型で許され、このコードはコンパイルできますが、name が初めて null になったときに実行時に TypeError でクラッシュします。この種のバグが strict を有効にしておく一番の理由です。
日常のコードでこれらの型が現れる場所:
| 発生源 | 型 |
|---|---|
arr.find(...) | T | undefined |
map.get(key) | V | undefined |
省略可能なプロパティ p?: T | 読むと T | undefined |
省略可能な引数 x?: T | 関数の中で T | undefined |
str.match(re) | RegExpMatchArray | null |
JSON.parse(text) | any なので何もチェックされない |
null と undefined をチェックする
以下のチェックはどれも、ブロックの中で型を絞り込みます。除外したいものに合ったチェックを選びましょう。
| チェック | 型から取り除くもの |
|---|---|
x !== undefined | undefined |
x !== null | null |
x != null | null と undefined |
typeof x !== "undefined" | undefined |
if (x) | null と undefined、さらに値 0、""、false、NaN も除外する |
== null は緩い等価比較が慣用的に使われる唯一の場所です。ちょうど null と undefined のときだけ true になり、それ以外では true になりません。truthiness のチェックだと空文字列も欠けているものとして扱ってしまい、それはしばしばバグになります。
オプショナルチェーン: ?.
a?.b は、a が null でも undefined でもなければ b を読み、そうでなければそこで止まって undefined を返します。同じ演算子はインデックス a?.[i] や呼び出し fn?.() にも使えます。
bob.address?.city の型は string | undefined です。オプショナルチェーンは結果に undefined を加えるので、ふつうは ?? と組み合わせます。実行時の動きは普通のJavaScriptです。短絡評価の詳細は オプショナルチェーン を参照してください。
Null 合体演算子: ??
a ?? b は、a が null か undefined でなければ a を、そうなら b を返します。0、""、false、NaN も捨ててしまう古い書き方 a || b の代わりになります:
| 左辺の値 | left || "d" | left ?? "d" |
|---|---|---|
null | "d" | "d" |
undefined | "d" | "d" |
0 | "d" | 0 |
"" | "d" | "" |
false | "d" | false |
NaN | "d" | NaN |
型の上では、?? は左辺から null と undefined を取り除き、残りと右辺のユニオン型を作ります。scores.get("Linus") ?? 0 を number に代入できるのはこのためです。
Null 合体代入: ??=
a ??= b は、a が null か undefined のときだけ a に b を代入します。仲間の ||= と &&= は、左辺が falsy または truthy のときに代入します。
retries: 0 は ??= をくぐり抜けますが、空の label は ||= で置き換えられます。opts.retries ??= 3 のあと、関数の残りでは opts.retries が number に絞り込まれます。
省略可能なプロパティと | undefined
nickname?: string と nickname: string | undefined は読むと同じですが、キーが存在しなければならないかどうかが違います:
呼び出し側がプロパティを省略してよいなら ? を、値が undefined であってもすべての呼び出し側に明示的に渡させたいなら | undefined を使います。オプション exactOptionalPropertyTypes(strict には含まれません)は ? をさらに厳しくします。nickname?: string は { nickname: undefined } を拒否し、キーがないか文字列の場合だけを受け付けるようになります。省略可能な関数の引数(x?: number)も省略可能なプロパティと同じように動き、関数の中では x は number | undefined です。
null と undefined のどちらを使うか
TypeScriptはどちらかを強制しませんが、1つのコードベースで両方を混ぜると、すべてのチェックで2つのケースを扱うことになります。よくある規約は次のとおりです:
- 自分の型での「未設定」には
undefined(と省略可能なプロパティ)を使う。JavaScriptがデフォルトで生み出すのはこちらです: 存在しないプロパティ、省略された引数、findやMap.getで見つからなかった場合。 - APIが
nullを返す場所ではnullを受け付ける:JSONにはundefinedがなく、String.prototype.matchや多くの DOM のメソッドはnullを返します。 - どちらもありうる値は
== nullでチェックする。
唯一の抜けは配列のインデックスアクセスです。users[5] は、インデックスが範囲外でも要素の型として型付けされます。noUncheckedIndexedAccess オプション(strict には含まれません)を使うと、すべてのインデックスアクセスに | undefined が加わり、コンパイラーがそれも捕まえるようになります。
よくある質問
TypeScriptの ?? はどういう意味ですか?
a ?? b はJavaScriptの Null 合体演算子です。a が null か undefined でなければ a を返し、そうなら b を返します。|| と違い、0、""、false のようなほかの falsy な値は残します。TypeScriptは左辺の型から null と undefined を取り除くので、a が string | undefined なら a ?? "x" は string です。
TypeScriptで値が undefined かどうかをチェックするには?
比較します: if (value !== undefined) { ... }。ブロックの中ではTypeScriptが型を絞り込みます。null と undefined の両方を1回のチェックで除外するには value != null(緩い等価比較)を使います。==/!= が慣用的に使われる唯一の場所です。truthiness のチェック(if (value))でも絞り込めますが、0、""、false も除外してしまいます。
「Object is possibly undefined」とはどういう意味ですか?
エラー TS18048('x' is possibly 'undefined')と TS18047('x' is possibly 'null')、また getUser().address.city のように値に単純な名前がない場合の TS2532(Object is possibly 'undefined')は strictNullChecks によるもので、型が undefined や null を含むのに、コードがそうでないかのように値を使っているという意味です。先にチェックするか、オプショナルチェーン(x?.name)を使うか、?? でデフォルト値を与えるか、値が本当に欠けることがないなら型を変えましょう。
TypeScriptの null と undefined の違いは何ですか?
それぞれ値を1つだけ持つ2つの別々の型です。JavaScriptは設定されていないもの(存在しないプロパティ、省略された引数、存在しないキーに対する Map.get)に undefined を使い、APIは意図的な「値なし」に null を使います(JSON、多くの DOM のメソッド)。TypeScriptは2つを別々に追跡するので、string | null は undefined を受け付けません。多くのコードベースでは、自分のコードでは undefined を選び、null は境界でだけ受け付けます。
省略可能なプロパティは | undefined と同じですか?
同じではありません。name?: string はプロパティがまったく存在しなくてもよいという意味で、読むと string | undefined になります。name: string | undefined は、値が undefined であってもプロパティが存在しなければならないという意味です。exactOptionalPropertyTypes を有効にすると、name?: string は明示的な undefined の値も受け付けなくなります。