keyof型演算子
keyof
はオブジェクトの型からプロパティ名を型として返す型演算子です。たとえば、name
プロパティを持つ型に対して、keyof
を使うと文字列リテラル型の"name"
が得られます。
ts
typePerson = {name : string;};typePersonKey = keyofPerson ;
ts
typePerson = {name : string;};typePersonKey = keyofPerson ;
2つ以上のプロパティがあるオブジェクトの型にkeyof
を使った場合は、すべてのプロパティ名がユニオン型で返されます。
ts
typeBook = {title : string;price : number;rating : number;};typeBookKey = keyofBook ;// 上は次と同じ意味になるtypeBookKey = "title" | "price" | "rating";
ts
typeBook = {title : string;price : number;rating : number;};typeBookKey = keyofBook ;// 上は次と同じ意味になるtypeBookKey = "title" | "price" | "rating";
インデックス型にkeyof
を使うと、インデックスキーの型が返ります。
ts
typeMapLike = { [K : string]: any };typeMapKeys = keyofMapLike ;
ts
typeMapLike = { [K : string]: any };typeMapKeys = keyofMapLike ;
キーがstring
のインデックス型は、string
ではなくstring | number
が返ります。number型のキーアクセスのobj[0]
はobj["0"]
と同じになるからです。
Mapped Typesにkeyof
を使うと、そのキーの型が返ります。
ts
typeMapLike = { [K in "x" | "y" | "z"]: any };typeMapKeys = keyofMapLike ;
ts
typeMapLike = { [K in "x" | "y" | "z"]: any };typeMapKeys = keyofMapLike ;
プロパティを持たないオブジェクトの型にkeyof
を使うとnever
型が返ります。
ts
typeWhat = keyof {};
ts
typeWhat = keyof {};
any
型にkeyof
を使うとstring | number | symbol
型が返ります。
ts
typeAnyKeys = keyof any;
ts
typeAnyKeys = keyof any;
keyofのメリット
keyof
のメリットは、保守性が上がる点です。オブジェクトの型とは別にプロパティ名のユニオン型を定義していると、オブジェクトの型のプロパティを変更したときに、そのユニオン型のほうも修正が必要になります。keyof
を使って、オブジェクトの型からキーを導出するようにしておけば、変更箇所はオブジェクトの型のところだけになります。
加えて、プロパティが何十個もあるようなオブジェクトを想像してみてください。そのプロパティ名のユニオン型を定義する必要が出てきたとします。その際に、プロパティ名をすべて転記するとなると、転記漏れや書き間違いもあるでしょう。そういう場合はkeyof
を使うとそもそも書き写す必要がないため、便利な上に安全なコーディングができます。
keyofはMapped Typesと一緒に使われる
keyofは単体で使うことよりMapped Typesと組み合わせて使われることが多いです。
📄️ Mapped Types
インデックス型では設定時はどのようなキーも自由に設定できてしまい、アクセス時は毎回undefinedかどうかの型チェックが必要です。入力の形式が決まっているのであればMapped Typesの使用を検討できます。