「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;
};
TypeScripttypeof MESSAGES は、「MESSAGES という値の形を、そのまま型にしたもの」です。
keyof で「型からキー」を取り出す
次に、keyof をかけます。
type MessageKey = keyof MessagesType;
// "hello" | "goodbye"
TypeScriptkeyof 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";
}
TypeScriptRoleValue を使えば、「値側だけを型として使う」こともできます。
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;
TypeScripttypeof で「その値の型」を取り出す:
type ObjType = typeof OBJ;
// { readonly a: 1; readonly b: 2; readonly c: 3 }
TypeScriptkeyof で「その型のキー」を取り出す:
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 はただの記号ではなく、「値と型をきれいに結びつけるための、かなり強力な橋」に見えてきます。
