keyof typeofで作ったキーの型は、存在しないキーを渡すとエラーになる
keyof typeof objは、実行時のオブジェクトobjが実際に持つキーだけを表すunion型(リテラル型の集合)になります。定義時に存在しないキー文字列を渡すと、その型に含まれないためエラーになります。
エラーメッセージの読み方
main.ts(6,22): error TS2345: Argument of type '"blue"' is not assignable to parameter of type '"red" | "green"'.
main.ts- ファイル名
6- 行番号
22- 列番号 — この位置でtscが検査に失敗しました
TS2345- エラーコード — 検索するとTypeScriptの解説が見つかります
Argument of type '"blue"' is not assignable to parameter of type '"red" | "green"'.- 内容 — 期待していたもの、または受け付けられなかったもの
このエラーが出る典型パターン
パターン1
1 const colors = { red: "#f00", green: "#0f0" }; 2 type ColorKey = keyof typeof colors; 3 function getColor(key: ColorKey): string { 4 return colors[key]; 5 } 6 console.log(getColor("blue")); ^
ColorKeyはcolorsが実際に持つキー("red" | "green")だけを表します。"blue"はcolorsに存在しないキーです。
直し方: "blue" を "red" にします。
パターン2
1 const rates = { jpy: 1, usd: 150 }; 2 type CurrencyKey = keyof typeof rates; 3 function getRate(key: CurrencyKey): number { 4 return rates[key]; 5 } 6 console.log(getRate("eur")); ^
CurrencyKeyはratesの実際のキーだけです。"eur"はratesに存在しないので渡せません。
直し方: "eur" を "usd" にします。
パターン3
1 const sizes = { small: 1, large: 3 }; 2 type SizeKey = keyof typeof sizes; 3 function getSize(key: SizeKey): number { 4 return sizes[key]; 5 } 6 console.log(getSize("medium")); ^
sizesには"medium"というキーはありません。SizeKeyは実際に存在するキーの集合に限られます。
直し方: "medium" を "small" にします。
パターン4
1 const fruits = { apple: 1, banana: 2 }; 2 type FruitKey = keyof typeof fruits; 3 function getCount(key: FruitKey): number { 4 return fruits[key]; 5 } 6 console.log(getCount("cherry")); ^
FruitKeyはfruitsが実際に持つキーだけを表します。"cherry"はfruitsに存在しないキーです。
直し方: "cherry" を "apple" にします。
パターン5
1 const codes = { ok: 200, notfound: 404 }; 2 type CodeKey = keyof typeof codes; 3 function getCode(key: CodeKey): number { 4 return codes[key]; 5 } 6 console.log(getCode("error")); ^
codesには"error"というキーはありません。CodeKeyは実際に存在するキーの集合に限られます。
直し方: "error" を "ok" にします。
よくある誤解
「キーはただの文字列だから、存在確認は実行時にやればいい」という発想は、keyof typeofパターンでは不要になります。存在しないキーの指定自体を、実行前にtscが検出してくれます。
実務での勘所
ここで使われているtypeofは、実行時にJavaScriptが返す"object"のような文字列を得るtypeof演算子とは別物です。型を書く位置(型注釈やジェネリクスの中)で使われるtypeofは、TypeScript独自の構文で、ある変数が持つ静的な型そのものを取り出します。keyofはその型が持つプロパティ名を、リテラル型のunionとして取り出す演算子です。同じtypeofという単語でも、値を書く場所と型を書く場所とでまったく違う意味になる点に注意が必要です。