Programming Field

null - TypeScriptキーワード一覧

スポンサーリンク

「null」キーワードは以下の2つの意味で使用されます。

いずれも「null 値」に関するものであるため、このページでは両者について説明しています。

概要

「null」キーワードのうち「null 値」を表す識別子「null」は、ECMAScriptの仕様で以下のように定義されています。

一方型名としての「null」は以下のようにされています。(特筆の無いものは TypeScript/spec.md 「3.2.6 The Null Type」での定義によります。

キーワード情報

詳細

「null」はその名の通り「null 値」を取り扱う際に用いるキーワードです。ECMAScript(JavaScript)では「null 値」を明示的に表すキーワードとして定義されていますが、TypeScriptではそれに加えて「null 型」を示すキーワードとしても使用されます。

「null 型」は、明示的に宣言された場合は「null 値」のみを設定することができる型となります。

let x: null;               // null 型変数
x = null;                  // null 値は null 型変数に代入可能
x = 10;                    // ERROR: TS2322: 数値を「null 型」に代入することができない

「strictNullCheck」が有効になっていない場合、「null 型」は「undefined 型」を除くあらゆる型の部分型の扱いになるため、「undefined 型」以外の型とunion type(共用体型)を作ると「null 型」の意味が失われます。逆にintersection type(交差型)を作ると「null 型」になります。

// 「strictNullCheck」が無効のとき
let y: number | null;      // number 型変数
let z: number & null;      // null 型変数

「null 型」が特に重要になるのは「strictNullCheck」が有効な場合です。このとき、「null 型」は他のあらゆる型と独立したような存在となります。

// 「strictNullCheck」が有効のとき
let y: number | null;      // number | null 型変数
let z: number & null;      // number & null 型変数(実質 never に近い型となり、このような記述をすることはまず無い)

また「strictNullCheck」が有効な場合、「null 型」とunion type(共用体型)を形成している型のデータに対してプロパティー参照や関数呼び出しなどを行おうとすると、「null の可能性がある」という旨の専用のエラーが発生します。

// 「strictNullCheck」が有効のとき
let fn: (() => void) | null = makeFunction();  // (() => void) | null 型変数
console.log(fn.toString());                    // ERROR: TS2531: null の可能性があるオブジェクト
fn();                                          // ERROR: TS2721: null の可能性がある関数の呼び出し

そのため、「null 型」を含む型のデータを扱う場合は、型ガードなどの型推論を用いて null を除去してから具体的な処理を行うのが基本となります。

// 「strictNullCheck」が有効のとき
let fn: (() => void) | null = makeFunction();  // (() => void) | null 型変数
// null の場合は実行されないような条件文(型ガードになる)
if (fn !== null) {
    // この中では「fn」は「() => void」型となるので、次の2行がコンパイル時エラーにならない
    console.log(fn.toString());
    fn();
}

// 渡した配列と同じ要素数の配列(中身は文字列または null)を返す何かしらの関数を呼び出す
let arr: Array<string | null> = queryNamesFromId([101, 106, 129]);
// 「arr」には「null 値」が含まれる可能性があるので、「null 値」を除去するために filter メソッドを用いる
let arrFiltered = arr.filter(
    // Array の filter メソッドのオーバーロードに
    // (ユーザー定義の)「型ガード関数」を受け付けるものがあるため、
    // それを用いて「null 値」の除去(val !== null で判定)と
    // 「null 型」の除去(「x is Y」の「Y」にあたる型が
    // 配列の要素の型になる)の両方を行うことができる
    // ※ 「typeof val」が「string | null」と推論されるので
    //    「Exclude<typeof val, null>」は「string」となる
    //    (TypeScript 2.8 以降で有効)
    (val): val is Exclude<typeof val, null> => (val !== null)
);
// 「arrFiltered」は「string[]」型となるので、「arrFiltered」に対して「null 値」を気にする必要がなくなる

なお、(値としての)「null」は真偽値としては false 扱い・「!null」の計算結果が「true」になることから、上記の「if (fn !== null) { ... }」は単純に「if (fn) { ... }」と書いても「null 型」を取り除く型ガードとして成立します。

※ 「false」扱いとなる値は「null 値」以外にもあるため、「if (x) { ... }」という記法が常に良いとは限りません。例えば「x: number | null」という変数に対して「null 値」を除去するつもりで「if (x) { ... }」とすると、x が 0 のとき if 文の中に入らなくなるため、「0」が何かしらの意味を持つ値である場合は注意が必要です。

また、型推論できないものの、実行環境の制約や直前の処理の結果などで「null 値(および undefined 値)ではない」ことが明らかである場合は、TypeScriptに用意された後置演算子「!」を用いると「null 型」(と「undefined 型」)を強制的に除去することができます。

// 「strictNullCheck」が有効のとき

// 何かしらのDOM要素を生成する関数を呼び出す
const someElement = createSomeElement();
// 「app」というIDを持つDOM要素を取得する
// (「appElement」は「HTMLElement | null」型になる)
const appElement = document.getElementById('app');
// 「このスクリプトは『app』というIDが必ず存在する状況下で実行される」として
// 「appElement」の型に「null 型」が無いものとするために「!」を付ける
appElement!.insertBefore(someElement, appElement!.firstChild);

※ 上記の例の場合は、以下のように「!」を付加する位置を「getElementById(...)」にすることもできます。

// 「strictNullCheck」が有効のとき

// 何かしらのDOM要素を生成する関数を呼び出す
const someElement = createSomeElement();
// 「app」というIDを持つDOM要素を取得する
// 「このスクリプトは『app』というIDが必ず存在する状況下で実行される」として
// 「appElement」の型に「null 型」が無いものとするために「!」を付ける
// (「appElement」は「null 型」が除去され「HTMLElement」型になる)
const appElement = document.getElementById('app')!;
appElement.insertBefore(someElement, appElement.firstChild);

「null 型」を除去する目的で「!」を使うのは「null ではないことが自明である」「null であることは完全に想定外なので実行時エラーとなっても良い」といったような場合に限るべきであり、また必要に応じてそのことが分かるようなコメントをソースコード上に記述しておくと良いと思われます。

このように「null 型」(および「undefined 型」)と「strictNullCheck」コンパイラーオプションを組み合わせることで、「null の可能性がある」ということをコンパイル時点でチェックし、null かどうかにより気を付けることができるため、うまく記述することで「null チェックをし忘れて null 参照エラー」というある種のヒューマンエラーを防止することに役立てることができます。