2021/07/03

TypeScriptとnull/undefinedチェック

JavaScript のことを書いているサイトであればほぼ書かれていると思われる null/undefined チェック問題。問題というよりは FAQ なんだろうけど、私は何度やっても覚えられない。


まず、 null と undefined の型だが、これはオブジェクトではなくプリミティブ型とのこと。昔ちょっと JavaScript を書いたときは 'undefined' と文字列になっていたような気がするが、そう書いてある()からそうなんだろう。

にもかかわらず、 typeof null は "object" だ。 typeof undefined は "undefined" である。
nullは「プリミティブ値」と表現されていたし、undefined も同様だ

Primitive (プリミティブ) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
https://developer.mozilla.org/ja/docs/Glossary/Primitive

undefinedはプリミティブデータ型なので typeof で "undefined" になるが、 null は「プリミティブに見える」と微妙な表現をされている特殊なオブジェクトだそうな。あらゆるオブジェクトは nullから派生するらしい。

 

等値演算子(==, !=)を使う場合、null と undefined は同類と見なされる。

01: let val = null;
02: 
03: if (val) {
04:   console.log('defined');
05: } else {
06:   console.log('undefined');
07: }

これはどっちを通ったかというと、"undefined" の方を通った。 1行目が undefined でも同じだ。

null と undefined を区別したいなら同値演算子(===, !==)を使う。

A user name of null?
https://developer.mozilla.org/ja/docs/Learn/Getting_started_with_the_web/JavaScript_basics#a_user_name_of_null

この例は (!myName || myName === null) となっているのだが、 !myName だけでよいのでは?
もし && だったら myName === null だけでよいだろうし。

 

  • undefined はプリミティブデータ型でありプリミティブ値である
  • null はプリミティブ値のようでありオブジェクトのようであり、特殊な扱い
  • undefined と null を同じ扱いにして良いなら等値演算子(==, !=)で扱える
    • x == null とか x == undefined とか
    • !x でもよい
  • undefined かどうかを調べたいなら typeof === 'undefined' にする
    • null の typeof は 'object' になる

 

ちなみに、変数に undefined を与えて console.log したのが上、 'undefined' を与えて console.log したのが下。

image

null だとどっちで与えても白い文字で出力された。 うーん。

console.log なのか JavaScript 全体なのか分からないけど、人間寄りになるように動いてしまうのが苦手意識を持ってしまう原因かもしれん(私がね)。


TypeScript は型定義があるため、もうちょっと複雑になりそうだ。

nullとundefined - TypeScript Deep Dive 日本語版
https://typescript-jp.gitbook.io/deep-dive/recap/null-undefined

等値演算子で null チェックすればよいよ、ということだ。

といっても、ここは TypeScript の仕様説明ではないので、もうちょっと確認しよう。

 

まず、 null が型の方として扱われるようだ。

let val: number = null;

これはコンパイルエラーだ。どうしても代入したければ、私は null も OKですよ、ということにするしかないのか。

let val: number | null = null;

ちなみに、これもダメだ。

let val: number = null as number;

 

null は型扱いなので、変数を null型で指定できるようだ。

let val: null = null;
val = 'hello';

こんなのは代入する行でコンパイルエラーになる。 undefined もそうだった。

let val: undefined;
val = null;

使い道はないけどね。

 

だから、 null や undefined を代入したいのなら型のところで | null のような形にすることになる。
なるのだが、 undefined はデフォルト値になるので、ちょっと特殊な扱いかもしれない。その代わり、代入しないまま使おうとしたら、 | undefined になっていない限りはエラーになる。型と異なる変数が入っている、という扱いかもしれないが、TS2454 コンパイルエラーは before being assigned なので別エラーにはなっている。


C言語をよく使う人からすると、使い終わった変数に null を代入したいという気持ちに駆られることがある。

開いたファイルを扱うオブジェクトの変数があって、その変数が null だったら先にオープンしてから処理を続ける、みたいな。それをやろうとすると、最初は undefined だからよいとして、一度オープンして、クローズしたら null にしたくなる。しかし null を代入させるなら | null で許容させる必要がある。

私の期分の問題かもしれないが、 | null というか、型を複数割り当てるというのがどうも苦手だ。 string | number とかされたら「どっちが入ってんだよ!」ってなるからやらないけど、似たような気持ちになってしまう。

なら指定しなければ良いのだが、そうじゃなかったら boolean の変数を用意して判断するみたいなことになるだろう。でも、そこまでするのはなんか大げさな気がしてしまう。

なので、あまり選びたくない2択のどちらかをせねばならぬ。
あるいは、そういう設計の仕方自体が JavaScript らしくないということかもしれない。

 

そういう、言語仕様ではなく、センスというかお作法というかがよくわかっていないというのが最大の悩みであった。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。