Q1typeof null は何を返しますか?
データ型まとめ — typeof と判定の早見表
ここまでに出てきた値を型の観点で整理します。7 つのプリミティブと Object の分類、typeof の戻り値、null と配列が object になる理由、数値として扱えるかの判定までまとめます。
JavaScript で扱う値は、文字列・数値・真偽値・配列・オブジェクト・null・undefined・Map・Set と多岐にわたります。実務では、フォームやサーバーから届いた値が期待した型かどうかを確かめてから処理を進めます。
この記事では、それらを型という 1 つの観点で整理し、typeof の結果と、型ごとの正しい確かめ方をまとめます。
型は 7 つのプリミティブと Object に分かれる
型が分かると、その値に対して何ができるかが決まります。文字列なら length や slice が使え、数値なら計算ができ、配列なら push が使えます。
逆に、届いた値の型を取り違えたまま処理を進めると、undefined や NaN が紛れ込んだことに気づかないまま、処理が最後まで進んでしまいます。
JavaScript の値は、プリミティブ(数値・文字列・真偽値・null・undefined・symbol・bigint の 7 つ。オブジェクトや配列ではない値)と Object(プロパティを持つ値。配列・Map・Set もここに含まれる)に分かれます。
この講座で扱ってきたのは、プリミティブのうち前の 5 つと Object です。
残る 2 つのうち symbol(重複しない一意のキーを作るための値)は特殊なキーを作る場面で、bigint(非常に大きな整数を専用に扱うための値)は Number では正確に表せない桁の整数を扱う場面で使います。どちらもこの講座の範囲では登場しないので、名前と位置づけだけ押さえておけば十分です。
型を調べる演算子が typeof です。値の前に書くと、型の名前を文字列で返します。
返る文字列は決まっていて、"string" "number" "boolean" "undefined" "symbol" "bigint" "object" "function" の 8 種類です。"function" は関数を渡したときだけ返る結果で、関数は次のカテゴリで扱います。
| 値の例 | 分類 | typeof の結果 |
|---|---|---|
| ORD-6033 のような文字列 | プリミティブ | string |
| 9800 のような数値 | プリミティブ | number |
| true と false | プリミティブ | boolean |
| 値を入れていない変数 | プリミティブ | undefined |
| null | プリミティブ | object |
| オブジェクトと配列 | Object | object |
console.log(typeof "ORD-6033"); // string
console.log(typeof 9800); // number
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof { orderId: "ORD-6033" }); // object
console.log(typeof [1, 2]); // object
// null もプリミティブだが typeof は object を返す
console.log(typeof null); // object
typeof が object を返す 3 つ — null と配列とオブジェクト
サーバーから届いた値が「配送先オブジェクト」なのか「まだ選ばれていない null」なのか「商品の配列」なのかで、次にする処理は変わります。ところが typeof はこの 3 つに同じ "object" を返すので、判定にそのまま使えません。
typeof null が "object" を返すのは、JavaScript の最初期からある動作です。仕様上の誤りとして知られていますが、直すと既存のコードが壊れるためそのまま残っています。
null かどうかは value === null で直接調べます。
配列かどうかは Array.isArray(value) で調べます。配列を渡したときだけ true を返すので、オブジェクトや null と区別できます。
この 2 つを先に除けば、残った "object" が「配列でも null でもないオブジェクト」です。
typeof は object です。値ごとに別の書き方で見分けます。どちらでもなければオブジェクトです。const shipping = null;
const cart = ["マウス"];
const order = { orderId: "ORD-6033" };
// typeof は 3 つとも同じ結果を返す
console.log(typeof shipping); // object
console.log(typeof cart); // object
// null は === で直接調べる
console.log(shipping === null); // true
// 配列は Array.isArray で調べる
console.log(Array.isArray(cart)); // true
console.log(Array.isArray(order)); // false
// 配列でも null でもないオブジェクトかどうか
console.log(typeof order === "object" && order !== null && !Array.isArray(order)); // true
null を先に除いてから中身を読む
typeof value === "object" だけを条件にして中身を読むと、value が null のときに TypeError で止まります。null は typeof では素通りしてしまうので、value !== null を先に確かめるか、value?.city のようにオプショナルチェーンを挟みます。
文字列を数値として扱えるか — Number と Number.isNaN
ここまでは typeof で型そのものを見分ける方法を扱いましたが、型がわかっても中身が期待どおりとは限りません。フォームから届く数量や金額は、見た目が数字でも型は文字列です。計算に使う前に「数値として扱えるか」を確かめないと、"12px" のような値がそのまま計算に流れて NaN を生みます。
ここでは型そのものではなく、変換できるかどうかを調べます。
Number(input) で変換し、結果が NaN かどうかを Number.isNaN(変換結果) で調べる方法は型変換の記事で扱いました。ここで型の判定と切り分けたいのは、これは型の確認ではなく中身の確認だという点です。
"9800" も "abc" も typeof はどちらも "string" で、数量として使えるかどうかは型からは分かりません。
落とし穴は空文字列です。Number("") は NaN ではなく 0 を返すため、未入力のフォームが「数量 0 の有効な入力」として通ってしまいます。
空文字列を弾きたいときは、変換の前に input !== "" を確かめます。
NaN です。空文字列だけは 0 になるので、変換前に弾きます。console.log(Number("9800")); // 9800
console.log(Number("abc")); // NaN
console.log(Number("")); // 0(NaN ではない)
// 変換した結果が NaN かどうかを調べる
console.log(Number.isNaN(Number("abc"))); // true
console.log(Number.isNaN(Number("9800"))); // false
// 空文字列は 0 として通ってしまう
console.log(Number.isNaN(Number(""))); // false
判定の使い分け — 早見表
型を確かめる書き方は 1 つではありません。typeof で足りる型と、typeof の結果からさらに絞り込む必要がある型があり、文字列の中身が数値かどうかは型とは別の確認です。調べたいことから書き方を選びます。
typeof で決まるのは object 以外の型です。object が返ったときと、文字列の中身を見たいときは別の書き方に切り替えます。| 調べたいこと | 書き方 | 注意 |
|---|---|---|
| 文字列・数値・真偽値か | typeof value === "string" など | そのまま使える |
| 値が入っていないか | value === undefined | typeof でも判定できる。既定値を当てたいだけなら ?? (第 9 回) |
| null か | value === null | typeof では object になる |
| 配列か | Array.isArray(value) | typeof では object になる |
| ただのオブジェクトか | typeof が object かつ null と配列を除く | 3 つの条件を && でつなぐ |
| 数値として扱えるか | Number に変換して Number.isNaN で確認 | 空文字列は 0 になる |
理解度チェック
まずは1問ずつ答えてみましょう。
Q2配列かどうかを確かめる書き方はどれですか?
Q3Number("") は何を返しますか?