Null vs. Undefined

Last updated 10 days ago

JavaScript(と、TypeScript)は、nullundefinedという2つのボトム型(bottom type)があります。これらは異なる意味を持っています。

  • 初期化されていない: undefined

  • 現在利用できない: null

どちらであるかをチェックする

現実としては、あなたは両方とも対応する必要があります。==でチェックしましょう:

/// Imagine you are doing `foo.bar == undefined` where bar can be one of:
console.log(undefined == undefined); // true
console.log(null == undefined); // true
// You don't have to worry about falsy values making through this check
console.log(0 == undefined); // false
console.log('' == undefined); // false
console.log(false == undefined); // false

== nullを使ってundefinednullを両方ともチェックすることを推奨します。一般的に2つを区別する必要はありません。

function foo(arg: string | null | undefined) {
if (arg != null) {
// `!=` がnullとundefinedを除外しているので、引数argは文字列です
}
}

1つだけ例外があります。次に説明するルートレベル(root level)のundefinedの値です。

ルートレベル(root level)のundefinedのチェック

== nullを使うべきだと言ったことを思い出してください。もちろん、覚えているでしょう(今、言ったばかりなので)。それは、ルートレベルのものには使用しないでください。 strictモード(strict mode)でfooを使うとき、fooが定義されていないと、ReferenceError exceptionが発生し、コールスタック全体がアンワインド(unwind)されます。

strictモードを使うべきです...現実としては、TSコンパイラはモジュール(modules)を使うときに、自動的に"use strict";を挿入します...後で解説を行うので、詳細は省略します:)

変数がglobalレベルで定義されているかどうかを確認するには、通常、typeofを使用します:

if (typeof someglobal !== 'undefined') {
// someglobal is now safe to use
console.log(someglobal);
}

undefinedを使わないようにする

TypeScriptにおいて、あなたは、変数と構造を分離して型を記述することができます。下記のように書く代わりに:

function foo(){
// if Something
return {a:1,b:2};
// else
return {a:1,b:undefined};
}

型アノテーションを使用すべきです。

function foo():{a:number,b?:number}{
// if Something
return {a:1,b:2};
// else
return {a:1};
}

Nodeスタイルのコールバック

Nodeスタイルのコールバック関数(例: (err, somethingElse)=> {/* something */})は、エラーがなければ errnullを設定して呼び出されます。開発者は一般的にtruthyチェックを行います:

fs.readFile('someFile', 'utf8', (err,data) => {
if (err) {
// do something
} else {
// no error
}
});

独自のAPIを作成するときは、一貫性のためにnullを使用することは、良くはありませんが、問題ありません。とはいえ、できればPromiseを返すようにするべきです。そうすれば、errnullかどうかを気にかける必要はなくなります(.then.catchを使います)。

有効性(validity)の意味でundefinedを使用しない

ひどい関数の例:

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とシリアライズ

JSON標準では、nullのエンコードはサポートしていますが、undefinedのエンコードはサポートしていません。値がnullである属性を持つオブジェクトをJSONにエンコードするとき、その属性はnull値とともにJSONに含まれますが、値がundefinedである属性は完全に除外されます。

JSON.stringify({willStay: null, willBeGone: undefined}); // {"willStay":null}

結果として、JSONベースのデータベースは、nullはサポートしますがundefinedはサポートしないことがあります。null値を持つ属性はエンコードされるため、オブジェクトをエンコードしてリモートのデータストアに送る前に、ある属性値をnullにすることで、その属性を削除したいという意図を伝えることができます。

属性値をundefinedにすると、その属性名はエンコードされないため、データの保存と転送のコストを節約することができます。しかし、これによって値を削除することと値が存在しないことの意味づけが複雑になってしまいます。

結論

TypeScriptチームは、nullを使いません: TypeScriptコーディングガイドライン。 そして、問題は起きていません。 Douglas Crockfordはnullはbad ideaであり、誰もがundefinedだけを使うべきだと考えています。

しかし、Nodeスタイルのコードでは、Error引数にnullが標準で使われています。これは現在利用できませんという意味です。私は個人的に、ほとんどのプロジェクトにおいて、意見がバラバラのライブラリを使っていますが、== nullで除外するだけなので、2つを区別しません。