🐙

TypeScript nullとundefinedどちらを使う?

2022/08/24に公開

はじめに

TypeScriptでは「データがない」ことを表す型が2種類存在します。
違いや使い分けについてまとめたいと思います。

null型, undefined型とは

どちらも「データがない」ということを表すプリミティブな型です。

const hoge: null = null
const foo: undefined = undefined

発生タイミング

nullは開発者が意図的に指定した場合のみ設定されます。

// 開発者が指定する
const hoge = null

undefinedは変数の初期値がない場合や存在しないプロパティにアクセスした場合などに自動で設定されます。

// 開発者が指定する
const hoge = undefined

// 変数の初期値がない場合
let hoge;
console.log(hoge); // undefined

// 存在しないプロパティにアクセスした場合
const obj = { hoge: false }
console.log(obj.foo); // undefined

//配列の存在しないい要素にアクセスした場合
const array = ["test"]
console.log(array[1]) // undefined

//返り値の無い関数の場合
function func() {
  console.log("test")
}
console.log(func()) // undefined

nullまたはundefinedの判定

nullまたはundefinedであるか判定する場合には、等価演算子(==)を使用します。
なお、厳密等価演算子(===)を使用した場合は、nullとundefinedが区別されます。

const hoge = null
const foo = undefined

console.log(hoge == null)  // true
console.log(foo == null)   // true
console.log(hoge === null) // true
console.log(foo === null)  // false

オプショナルプロパティについて

undefinedと混同しがちなものにオプショナルプロパティがあります。
オプショナルプロパティとは、プロパティ名の後ろに?を付与することで、「あるかもしれないし、ないかもしれない」を表すことができるものです。

type MyObj = {
  hoge: boolean
  foo?: number  // number | undefined
}

// fooが存在してもしなくてもコンパイルエラーが発生しない
const obj: MyObj = { hoge: false, foo: 1234 }
const obj2: MyObj = { hoge: true }
const obj3: MyObj = { hoge: true, foo: undefined }

オプショナルプロパティが付与されたプロパティは自動的に、undefinedとのユニオン型が定義されます。
しかし、明示的にundefinedとのユニオン型を指定した場合に挙動が異なります。

type MyObj = {
  hoge: boolean
  foo: number | undefined
}

// fooを省略した場合コンパイルエラーが発生する
const obj: MyObj = { hoge: false, foo: 1234 }
const obj2: MyObj = { hoge: true }  // コンパイルエラー
const obj3: MyObj = { hoge: false, foo: undefined }

オプショナルプロパティを使用するメリットは、すべてのプロパティを定義する必要がなく利便性が高いことにあります。
逆に言えば、書き忘れか省略かがわからないためミスに気づくことができなくなります。

なお、TypeScript4.4以降、exceptOptionalPropertyTypesが有効な場合、オプショナルプロパティを使用しているとき明示的にundefinedを代入できないようになりました。
?が省略可能かどうかのみを表すようになるのでわかりやすくなります。

type MyObj = {
  hoge: boolean
  foo?: number
}

const obj: MyObj = { hoge: false, foo: 1234 }
const obj2: MyObj = { hoge: true }
const obj3: MyObj = { hoge: true, foo: undefined }  // コンパイルエラー

null型, undefined型の違い

typeof演算子

typeof演算子の結果が異なります。
nullの場合objectを返すのが直感と反します。

const hoge = null
const foo = undefined

console.log(typeof hoge) // "object"
console.log(typeof foo)  // "undefined"

JSON.stringify()

JSON文字列への変換時に、値がnullの場合はnullとして変換されますが、値がundefinedであるプロパティは完全に除外されます。

JSON.stringify({hoge: null, foo: undefined}); // {"hoge":null}

どう使い分けるか

TypeScript開発チームのコーディングガイドでは、undefinedを使用するよう記載されています。

Use undefined. Do not use null

undefinedが自動的に設定されることを考慮すると、原則undefinedに寄せる方がルールの統一がしやすくなると思います。
ただし、DOM系のAPIではnullが使用されることもあるので、その場合は柔軟に対応しましょう。

https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined

おわりに

今までなんとなく使用していたnull, undefinedも改めて調べてみました。
どちらを使うにしても自分なりの理由を持ちたいものです。

最後に宣伝ですが、sweeepではエンジニアを募集しています。ご興味のある方は下記リンクよりご応募お待ちしております!

https://corp.sweeep.ai/recruit

参考

https://gihyo.jp/book/2022/978-4-297-12747-3
https://typescript-jp.gitbook.io/deep-dive/

GitHubで編集を提案

Discussion