JavaScript, TypeScriptでは値がないことを示す値として、多くの言語にもあるnull
に加えてundefined
もあります。
使い分けについてとやかくいうつもりはありませんが、傾向として設定していないときの「ない」に関してはundefined
を値として使うことが多いので意図的に「ない」を意味したいときはnull
を使うほうが他のエンジニアに対して「ない」ことにしていることを伝えられるでしょう。
それがundefined
であるか ?
ある変数定数がundefined
であるかの判定は普通に考えれば以下です。
const ifUndefined = (variable: unknown): variable is undefined => {
return variable === undefined;
};
これはもちろん正しいのですが、歴史的背景から他の方法も存在します。
歴史的な背景
undefined
は今でこそ定数となりましたが、かつてはglobalにある変数でした。そのため上書きができました。
undefined = '👶';
上書きされてしまうと前項で述べた方法ではその値がundefined
であるかはわかりません。
どうやって判定しますか
プリミティブ型の型判定に使うtypeof
を使います。
const ifUndefined = (variable: unknown): variable is undefined => {
return typeof variable === 'undefined';
};
これは現在undefined
が上書きされているかどうかを問いません。もしも古いES(ES5以前)でも使えるようなパッケージを公開することが視野にある場合は単なる等値比較ではなく、こちらを使うようにするのが無難です。
上書きされたundefined
は元に戻りませんか
以下のようにして戻すことができます。
undefined = void 0;
void
を使います。void 0
としていますがこの0
はどのような値でも問題ありません。
console.log(typeof void 0 === 'undefined');
// true
console.log(typeof void 1 === 'undefined');
// true
console.log(typeof void '👶' === 'undefined');
// true
console.log(typeof void null === 'undefined');
// true
console.log(typeof void new Error() === 'undefined');
// true
このvoid
はTypeScriptで使われる型のvoid
とは異なるものです。
おまけ
null
null
はundefined
と異なり、初めから定数値(リテラル)として定義されているため、上書きができません。そのため直接等値比較ができます。
const isNull = (variable: unknown): variable is null => {
return variable === null;
};
逆に
typeof null
としても'null'
という文字列を得ることはできません。typeof null
は'object'
となってしまいます。
console.log(typeof null);
// 'object'
これはTypeScriptでtypeof
をやったことがあるかたはおわかりいただけるかと思います。typeof variable === 'object'
としたときのif, switch
のブロックはvariable
の型はobject | null
です。
if (typeof variable === 'object') {
// variable may be object | null
}
そのため、変数定数がobject
であることを確定させたいときはtypeof
のあとにnull
かどうかの等値比較をする必要があります。
const isObject = (variable: unknown): variable is object => {
if (typeof variable === 'object') {
// object | null
if (variable !== null) {
// object
return true;
}
// null
}
// unknown
return false;
};
この比較の順番を逆にするとTypeScriptは変数定数がobject
であることを確定させることができません。これはTypeScriptのType predicate
が関係しています。
const isObject = (variable: unknown): variable is object => {
if (variable !== null) {
// unknown
if (typeof variable === 'object') {
// object | null
return true;
}
// unknown
}
// unknown
return false;
};
TypeScriptが解釈しているif
の時点での型をコメントで書いてありますので参照ください。なお、この関数isObject()
はTypeScriptによる型の絞り込みは失敗していますが、両方とも同じように動作します。