TypeScript

typeof演算子の使い方|JavaScriptの型判定とTypeScriptのtypeof型・keyof型

typeofは同じ綴りで2つの別物を指します。JavaScriptのtypeofは値を受け取り、その型名を文字列で返す実行時の演算子です。一方TypeScriptのtypeofは値から型を取り出す型レベルの演算子で、動きも使う場所もまったく違います。この記事では、JavaScriptのtypeofによる型判定と落とし穴、TypeScriptのtypeof型・keyof型、そして両者を組み合わせるkeyof typeofイディオムまでを、実行結果つきのコード例で整理します。

まとめ:typeofの要点(先に結論)

  • JavaScriptのtypeofは値を受け取り、"string""number"など8種類の型名を文字列で返す(実行時)。
  • typeof null"object"、配列も"object"を返す。配列判定はArray.isArray()、その他の内部型はObject.prototype.toString.call()を使う。
  • 未宣言の変数に対してもtypeofはエラーを投げないため、存在チェックに使える。
  • TypeScriptのtypeofは型を書く位置で使い、type T = typeof objのように値から型を生成する(コンパイル時)。
  • keyofは型を受け取り、そのプロパティ名のユニオン型を作る。keyof typeof objで「実行時のオブジェクトのキー集合」を型として得るのが定番。

JavaScriptのtypeof演算子とは(値の型を文字列で返す)

JavaScriptのtypeofは、直後に置いた値の型を小文字の文字列で返す単項演算子です。書き方はtypeof xtypeof(x)のどちらでも同じ結果になります。返り値は必ず文字列なので、typeof x === "number"のように等価比較で分岐に使います。

typeof "hello";      // "string"
typeof 42;           // "number"
typeof true;         // "boolean"
typeof undefined;    // "undefined"
typeof 10n;          // "bigint"
typeof Symbol();     // "symbol"
typeof {};           // "object"
typeof function(){}; // "function"

typeofが返す8種類の型名

typeofの返り値は次の8つに限られます。プリミティブ(string/number/bigint/boolean/undefined/symbol)はそれぞれ固有の文字列を返し、関数は"function"、それ以外のオブジェクトはすべて"object"にまとめられます。

値の例 typeofの返り値
“text”, “” “string”
42, NaN, Infinity “number”
10n “bigint”
true, false “boolean”
undefined, 未宣言の変数 “undefined”
Symbol() “symbol”
関数, class, アロー関数 “function”
{}, [], null, new Date() “object”

注意点は2つ。NaNは「非数」という名前でもtypeof NaN"number"です。数値かどうかを厳密に見たいときはNumber.isNaN()Number.isFinite()を併用します。もう1つ、classで定義したものも実体は関数なので"function"を返します。

typeof nullが”object”になる歴史的仕様

もっとも有名な落とし穴がtypeof nullです。nullは「値が無い」ことを表すプリミティブですが、typeofは"object"を返します。

typeof null;   // "object"("null" ではない)

これはJavaScript最初期の実装に由来するバグで、後方互換のため修正されず仕様として残っています。nullを判定したいときはtypeofに頼らず、value === nullと直接比較するのが確実です。「nullでもundefinedでもない」を見たいときはvalue != null(緩い等価)で両方をまとめて弾けます。

配列・日付はすべて”object” ─ Array.isArrayで判定する

配列・日付・正規表現・プレーンなオブジェクトは、typeofではすべて"object"にまとめられ区別できません。配列かどうかは専用のArray.isArray()を使います。

typeof [1, 2, 3];         // "object"(配列と分からない)
Array.isArray([1, 2, 3]); // true ← 配列判定はこちら
Array.isArray("abc");     // false

配列以外の内部型まで細かく見分けたいときはObject.prototype.toString.call()が使えます。"[object Array]""[object Date]""[object RegExp]"のように内部クラス名を返すため、typeofよりも解像度が高い判定ができます。

typeofでJavaScriptの変数の型を判定する

実務でtypeofを使う場面の中心が、変数や引数の型チェックです。文字列・数値・真偽値・undefinedはtypeofだけで安全に判定できます。

function formatValue(value) {
  if (typeof value === "string") return value.trim();
  if (typeof value === "number") return value.toFixed(2);
  if (typeof value === "boolean") return value ? "ON" : "OFF";
  return String(value);
}

未宣言の変数を安全に存在チェックする

typeofには他の演算子にない特性があります。宣言されていない変数に対してもReferenceErrorを投げず、"undefined"を返すという点です。

// someGlobal を直接参照すると未宣言なら例外だが…
if (typeof someGlobal !== "undefined") {
  // ここは変数が存在するときだけ安全に通る
  someGlobal.init();
}

このため、ライブラリやグローバル変数が読み込まれているかを確かめる「存在チェック」にtypeofが使われます。if (someGlobal)だと未宣言時にエラーになりますが、typeof経由なら落ちません。

typeofで見分けられないケースと使い分け

typeofで足りるのはプリミティブの大分類までで、オブジェクトの中身の型までは判定できません。用途で道具を切り替えてください。

判定したいもの 使う手段
文字列・数値・真偽値・undefined typeof
配列かどうか Array.isArray()
null かどうか value === null
特定クラスのインスタンス instanceof
Date/RegExp などの内部型 Object.prototype.toString.call()
typeof new Date();          // "object"(Dateと分からない)
new Date() instanceof Date; // true ← クラス判定は instanceof

とくに外部APIのレスポンスのように「形が保証されない実行時のデータ」をtypeofで一つずつ確かめるのは避けるべきです。プロパティごとにtypeofを並べる検証は抜け漏れやすく、ネストした構造には対応しきれません。この用途にはスキーマ検証ライブラリが向いており、TypeScriptの型と実行時チェックを同時に得られます。導入手順はZodの使い方入門|TypeScript型安全バリデーションにまとめています。

TypeScriptのtypeof型(値から型を取り出す)

ここからは同じ綴りでも意味が変わります。TypeScriptでは、型を書く位置(型注釈やtypeエイリアスの右辺)にtypeofを置くと、その値の型を取り出して再利用できます。実行時には一切動かず、コンパイル時に型として展開されます。

const config = {
  host: "localhost",
  port: 8080,
};

// 値 config から型を取り出す(型位置の typeof)
type Config = typeof config;
// type Config = { host: string; port: number }

すでにある値を「正」として型を導出できるため、値と型を二重に手書きして食い違わせる事故を防げます。設定オブジェクトや定数テーブルの型を、定義そのものから同期させたいときに使います。

JavaScriptのtypeofとTypeScriptのtypeofの違い

混乱の元はこの2つが同じキーワードなのに正反対だという点です。区別のコツは「typeofがどこに書かれているか」です。式の中(値の位置)にあれば実行時のJavaScript、型注釈の中(値を期待していない位置)にあればTypeScriptの型演算子です。

観点 JavaScriptのtypeof TypeScriptのtypeof
受け取るもの 値(を型に変換)
返すもの 型名の文字列
いつ動くか 実行時 コンパイル時のみ
書く場所 式の中 型注釈・typeの右辺
const x = 10;

const a = typeof x;   // 値 "number"(実行時のJS)
type B = typeof x;    // 型 number(コンパイル時のTS)

as constと組み合わせてリテラル型を得る

TypeScriptのtypeofはas constと組み合わせると、幅の広いstringではなく具体的な文字列リテラル型が得られます。as constで値をリテラルとして固定し、その型をtypeofで取り出す流れです。

const theme = {
  primary: "#3366ff",
  radius: 8,
} as const;

type Theme = typeof theme;
// { readonly primary: "#3366ff"; readonly radius: 8 }

as constを付けない場合、primarystringradiusnumberまで広がってしまいます。定数の値そのものを型として使いたいときはas constとtypeofをセットで使います。

keyof演算子でオブジェクト型のキーをユニオン型にする

keyofはTypeScript専用の型演算子で、オブジェクト型を受け取り、そのプロパティ名を文字列リテラルのユニオン型にして返します。「この型が持つキーのどれか」を表す型を作れます。

type User = { id: number; name: string; email: string };

type UserKey = keyof User;
// "id" | "name" | "email"

keyof typeofイディオム(値からキーの型を得る)

keyofは型を受け取るため、手元にあるのが「型」ではなく「値(オブジェクト)」のときは、先にtypeofで型へ変換してからkeyofを適用します。このkeyof typeofの2段重ねが、実務でもっとも使うイディオムです。

const statusLabels = {
  active: "有効",
  inactive: "無効",
  pending: "保留",
};

// 値 → typeof で型 → keyof でキー集合
type StatusKey = keyof typeof statusLabels;
// "active" | "inactive" | "pending"

読み解く順番は内側からです。typeof statusLabelsでオブジェクトの型を取り出し、その型にkeyofを掛けてキーのユニオン型を得ています。定数テーブルのキーを型として使い回したいときの定番で、キーを増やせば型も自動で追随します。

型安全なプロパティアクセス関数を書く

keyofの典型的な使いどころが、ジェネリクスと組み合わせた型安全なプロパティ取得です。キーの型をK extends keyof Tで縛ることで、存在しないキーを渡すとコンパイルエラーになります。

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: "Sato" };

getProperty(user, "name"); // string 型で返る
getProperty(user, "age");  // エラー: "age" は keyof user にない

返り値の型T[K](インデックスアクセス型)により、nameを渡せばstringidを渡せばnumberと、キーに応じた正確な型が返ります。文字列を直接受け取る素朴な実装ではこの安全性は得られません。

よくある質問

typeof nullが”object”になるのはなぜですか?

JavaScript最初期の実装に由来する歴史的なバグで、後方互換性のために修正されず仕様として残っているためです。nullを判定するときはtypeofを使わず、value === nullと直接比較してください。

配列かどうかをtypeofで判定できますか?

できません。typeof [1,2,3]"object"を返し、通常のオブジェクトと区別できません。配列判定にはArray.isArray()を使ってください。より細かい内部型はObject.prototype.toString.call()で見分けられます。

JavaScriptのtypeofとTypeScriptのtypeofは同じものですか?

綴りは同じですが別物です。式の中で使うtypeofは実行時のJavaScriptで、値の型名を文字列で返します。型注釈やtypeの右辺で使うtypeofはTypeScriptの型演算子で、値から型を取り出します。書かれている場所で見分けてください。

keyof typeofはどんなときに使いますか?

実行時のオブジェクト(定数テーブルや設定オブジェクト)のキー集合を、型として使いたいときに使います。typeofで値を型に変換し、keyofでそのキーのユニオン型を得ます。キーを追加すれば型も自動で更新されます。

C#やJavaのtypeofとは違うのですか?

別物です。この記事で扱うのはJavaScript/TypeScriptのtypeofで、JavaScriptでは型名の文字列を返し、TypeScriptでは型を取り出します。C#やJavaのtypeof(型情報・クラスオブジェクトの取得)とは動作も用途も異なります。言語を取り違えないよう注意してください。

関連記事

資料請求

RELATED POSTS 関連記事