JavaScript(と、TypeScript)は、null
とundefined
という2つのボトム型(bottom type)があります。これらは異なる意味を持っています。
- 初期化されていない:
undefined
。 - 現在利用できない:
null
。
現実としては、あなたは両方とも対応する必要があります。==
でチェックしましょう:
/// `foo.bar == undefined` のようなコードを書いたときに、何が起きるか想像してみてください:
console.log(undefined == undefined); // true
console.log(null == undefined); // true
// このようなチェックをすれば、falsyな値について心配する必要はありません
console.log(0 == undefined); // false
console.log('' == undefined); // false
console.log(false == undefined); // false
== null
を使ってundefined
と null
を両方ともチェックすることを推奨します。一般的に2つを区別する必要はありません。
function foo(arg: string | null | undefined) {
if (arg != null) {
// `!=` がnullとundefinedを除外しているので、引数argは文字列です
}
}
1つだけ例外があります。次に説明するルートレベル(root level)のundefinedの値です。
== null
を使うべきだと言ったことを思い出してください。もちろん、覚えているでしょう(今、言ったばかりなので)。それは、ルートレベルのものには使用しないでください。 strictモード(strict mode)でfoo
を使うとき、foo
が定義されていないと、ReferenceError
exceptionが発生し、コールスタック全体がアンワインド(unwind)されます。
strictモードを使うべきです...現実としては、TSコンパイラはモジュール(modules)を使うときに、自動的に
"use strict";
を挿入します...後で解説を行うので、詳細は省略します:)
変数が_global_レベルで定義されているかどうかを確認するには、通常、typeof
を使用します:
if (typeof someglobal !== 'undefined') {
// これでsomeglobalは安全に利用できます
console.log(someglobal);
}
TypeScriptでは値と構造を分離してドキュメントのようにわかりやすくすることができます。下記のようなコードを想像してください:
function foo(){
// if 何らかの場合に返す値
return {a:1,b:2};
// else それ以外の場合に返す値
return {a:1,b:undefined};
}
これは、次のように型アノテーションを使用すべきです。
function foo():{a:number,b?:number}{
// if 何らかの場合に返す値
return {a:1,b:2};
// else それ以外の場合に返す値
return {a:1};
}
Nodeスタイルのコールバック関数(例: (err, somethingElse)=> {/* something */}
)は、エラーがなければ err
にnull
を設定して呼び出されます。開発者は一般的にtruthyチェックを行います:
fs.readFile('someFile', 'utf8', (err,data) => {
if (err) {
// 何らかのエラー処理をする
} else {
// エラーなし
}
});
独自のAPIを作成するときは、一貫性のためにnull
を使用することは、良くはありませんが、問題ありません。とはいえ、できればPromiseを返すようにするべきです。そうすれば、err
がnull
かどうかを気にかける必要はなくなります(.then
と.catch
を使います)。
下記は、ひどい関数の例です:
function toInt(str:string) {
return str ? parseInt(str) : undefined;
}
下記のようにする方が、はるかに良いでしょう:
function toInt(str: string): { valid: boolean, int?: number } {
const int = parseInt(str);
if (isNaN(int)) {
return { valid: false };
}
else {
return { valid: true, int };
}
}
JSON標準では、null
のエンコードはサポートしていますが、undefined
のエンコードはサポートしていません。値がnull
である属性を持つオブジェクトをJSONにエンコードするとき、その属性はnull
値とともにJSONに含まれますが、値がundefined
である属性は完全に除外されます。
JSON.stringify({willStay: null, willBeGone: undefined}); // {"willStay":null}
結果として、JSONベースのデータベースは、null
はサポートしますがundefined
はサポートしないことがあります。null
値を持つ属性はエンコードされるため、オブジェクトをエンコードしてリモートのデータストアに送る前に、ある属性値をnull
にすることで、その属性をクリアしたいという意図を明確に伝えることができます。
属性値をundefined
にすると、その属性はJSONにエンコードされないため、データのストレージと転送のコストを節約することができます。しかし、これによって、値をクリアすることと、値が存在しないことの文脈を曖昧にしてしまいます。
TypeScriptチームは、null
を使いません: TypeScriptコーディングガイドライン。 そして、問題は起きていません。 Douglas Crockfordはnullは良くない考えであり、誰もがundefined
だけを使うべきだと考えています。
しかし、Nodeスタイルのコードでは、Error引数にnull
が標準で使われています。これは何かがおかしいです
という意味です。私は個人的に、ほとんどのプロジェクトにおいて、意見がバラバラのライブラリを使っていますが、== null
で除外するだけなので、2つの区別について気にすることはありません。