TypeScript | 基礎文法:オブジェクト基礎 – keyof typeof の組み合わせ

TypeScript TypeScript
スポンサーリンク

「keyof typeof」は値から「キーの型」を取り出す最強コンボ

まず結論からいきます。
keyof typeof は、「実際のオブジェクト(値)から、そのプロパティ名の“型”を自動で作る」ための組み合わせです。

const MESSAGES = {
  hello: "こんにちは",
  goodbye: "さようなら",
};

type MessageKey = keyof typeof MESSAGES;
// "hello" | "goodbye"
TypeScript

ここで起きていることはこうです。

  • typeof MESSAGES → 「MESSAGES の“型”」を取り出す
  • keyof (その型) → 「その型が持つプロパティ名」をユニオン型として取り出す

つまり、「値 → 型 → キーの型」という流れを一気にやっているのが keyof typeof です。


一歩ずつ分解して理解する:typeof と keyof の役割

typeof で「値から型」を取り出す

const MESSAGES = {
  hello: "こんにちは",
  goodbye: "さようなら",
};

type MessagesType = typeof MESSAGES;
TypeScript

このとき MessagesType は、次のような型になります。

type MessagesType = {
  hello: string;
  goodbye: string;
};
TypeScript

typeof MESSAGES は、「MESSAGES という値の形を、そのまま型にしたもの」です。

keyof で「型からキー」を取り出す

次に、keyof をかけます。

type MessageKey = keyof MessagesType;
// "hello" | "goodbye"
TypeScript

keyof MessagesType は、「MessagesType が持つプロパティ名のユニオン型」です。
ここでは "hello" | "goodbye" になります。

これをまとめて書いたのが、

type MessageKey = keyof typeof MESSAGES;
TypeScript

というわけです。
「typeof で型にして、keyof でキーを抜き出す」——この二段階を一気にやるのがポイントです。


典型パターン1:定数オブジェクトから「キーの型」を作る

メッセージテーブルのキーを型にする

さっきの例を、もう少し実用的にします。

const MESSAGES = {
  hello: "こんにちは",
  goodbye: "さようなら",
  thanks: "ありがとう",
} as const;

type MessageKey = keyof typeof MESSAGES;
// "hello" | "goodbye" | "thanks"
TypeScript

ここで as const を付けているのは、
MESSAGES の値を「リテラルな "こんにちは" などとして扱う」ためですが、
keyof typeof のポイントは「キーの部分」です。

MessageKey を使うと、こんな関数が書けます。

function getMessage(key: MessageKey) {
  return MESSAGES[key];
}

getMessage("hello");   // OK
getMessage("thanks");  // OK
// getMessage("bye");  // エラー: '"bye"' は 'MessageKey' に含まれない
TypeScript

ここで守られているのは、

  • MESSAGES に存在しないキーを渡そうとするとコンパイルエラー
  • MESSAGES にキーを追加・削除すると、MessageKey も自動で追従

という状態です。

「定数オブジェクトのキーを“文字列”ではなく“型”として扱う」
これが keyof typeof の一番おいしい使い方です。


典型パターン2:enum っぽいオブジェクトからキーのユニオンを作る

役割(ロール)を定義して、そのキーを型にする

const ROLES = {
  ADMIN: "admin",
  USER: "user",
  GUEST: "guest",
} as const;

type RoleKey = keyof typeof ROLES;
// "ADMIN" | "USER" | "GUEST"

type RoleValue = (typeof ROLES)[RoleKey];
// "admin" | "user" | "guest"
TypeScript

ここでは二段階やっています。

  • keyof typeof ROLES で、「キー側(ADMIN / USER / GUEST)」のユニオン型
  • (typeof ROLES)[RoleKey] で、「値側(”admin” | “user” | “guest”)」のユニオン型

RoleKey を使えば、「キー名だけを受け取る」関数が書けます。

function isAdminKey(key: RoleKey) {
  return key === "ADMIN";
}
TypeScript

RoleValue を使えば、「値側だけを型として使う」こともできます。

function setRole(role: RoleValue) {
  // role は "admin" | "user" | "guest"
}
TypeScript

「オブジェクトを一つ書いておけば、キー側・値側の両方を型として再利用できる」
この設計ができるようになると、keyof typeof は一気に実務レベルの武器になります。


典型パターン3:設定オブジェクトのキーを安全に扱う

設定のキーを引数に取る関数

const CONFIG = {
  apiBaseUrl: "https://example.com",
  timeout: 5000,
  retry: 3,
} as const;

type ConfigKey = keyof typeof CONFIG;
// "apiBaseUrl" | "timeout" | "retry"

function getConfig(key: ConfigKey) {
  return CONFIG[key];
}
TypeScript

こうしておくと、

getConfig("apiBaseUrl"); // OK
getConfig("timeout");    // OK
// getConfig("baseUrl"); // エラー(スペルミスを防げる)
TypeScript

「設定オブジェクトのキーを、文字列の書き間違いなしで扱える」
これが keyof typeof の実務でのよくある使い方です。

さらに、ConfigKey を使って「設定のキー一覧」を型として扱うこともできます。

const keys: ConfigKey[] = ["apiBaseUrl", "timeout"];
// ["retry"] を足してもOK
TypeScript

「値 → typeof → keyof」の流れを頭に刻む

ここまでの話を、流れで整理します。

1つのオブジェクト(値)からスタートする:

const OBJ = {
  a: 1,
  b: 2,
  c: 3,
} as const;
TypeScript

typeof で「その値の型」を取り出す:

type ObjType = typeof OBJ;
// { readonly a: 1; readonly b: 2; readonly c: 3 }
TypeScript

keyof で「その型のキー」を取り出す:

type ObjKey = keyof ObjType;
// "a" | "b" | "c"
TypeScript

これを一気に書いたのが、

type ObjKey = keyof typeof OBJ;
TypeScript

という形です。

「値 → typeof → keyof」
この3ステップを、頭の中でスッとトレースできるようになると、
keyof typeof はもう怖くなくなります。


初心者がまず掴んでおきたい keyof typeof の感覚

最後に、感覚的なまとめをします。

keyof typeof は、
「このオブジェクトの“キーの一覧”を、型として欲しい」
と思ったときに使う合言葉です。

その結果として、

  • 定数オブジェクトのキーを安全に扱える
  • キーの追加・削除に型が自動で追従する
  • 文字列の書き間違い(スペルミス)をコンパイル時に潰せる

という状態が手に入ります。

まずは小さく、こんなところから始めてみてください。

const COLORS = {
  red: "#f00",
  green: "#0f0",
  blue: "#00f",
} as const;

type ColorKey = keyof typeof COLORS;

function getColor(key: ColorKey) {
  return COLORS[key];
}
TypeScript

「値を書いたら、そのキーを型としても使える」
この感覚が身体に入ってくると、
keyof typeof はただの記号ではなく、「値と型をきれいに結びつけるための、かなり強力な橋」に見えてきます。

タイトルとURLをコピーしました