型定義でkeyof, typeof、またkeyofとtypeofが混ざって使われるケースを整理してみました。
keyof
オブジェクトの型からプロパティ名(key名)を型として返す型演算子です。
2つ以上のプロパティがあるオブジェクトの型にkeyofを使った場合は、すべてのプロパティ名がユニオン型で返されます。
以下のサンプルで、オブジェクトのキーが文字列リテラルのユニオン型を生成できる。
自分で定義しても同じですが、この方法を使うのが一般的です。
type Hoge = {
name: string;
age: number;
gender: string;
}
// Hogeのキーである'name' | 'age' | 'gender'の文字列リテラルのユニオン型が生成される
type SampleType = keyof Hoge
const data1: SampleType = "age" // OK
const data2: SampleType = "job" // NG (name, age, gender以外はエラーになる)
// 生成したユニオン型に別の文字列を追加して汎用的に使い回す
type SampleTypeWithJob = keyof Hoge | "job"
const data3: SampleTypeWithJob = "job" // OK
// 生成したユニオン型から不要な値を除去して使う
type SampleTypeWithoutAge = Exclude<SampleType, 'age'>
const data4: SampleTypeWithJob = "age" // NG
typeof
typeof 演算子は対象の値のデータ型を表す文字列を返す演算子。
データ型を調べることができます。
const hogeStr = "hogehoge"
// typeof hogeStr を実行すると、'string' が返される
const hogeNum = 1234
// typeof hogeNum を実行すると、'number'が返される
以下は型定義でよくあるユースケース
// 配列データ
const size = ['small', 'medium', 'large'] as const
// 'small' | 'medium' | 'large'の文字列リテラルのユニオン型が生成される
type Size = typeof size[number]
const a = 'small' // OK
const b = 'extraLarge' // NG!!
// オブジェクトの配列
const RANK_NAME_POINTS = [
{ rank: 'bronze', point: 0 },
{ rank: 'silver', point: 1500 },
{ rank: 'gold', point: 3200 },
] as const
// "bronze" | "silver" | "gold" の文字列リテラルのユニオン型が生成される
type RankNameType = typeof MEMBERS_RANK_NAME_POINTS[number]['rank']
// これに別の文字列を足すことも勿論可能
type MembersRankNameType2 = typeof MEMBERS_RANK_NAME_POINTS[number]['rank'] | 'free'
keyofとtypeofを混合で使うケース
これもよく見かけますが、紐解いてみると仕組みは簡単です。
const hoge = {
name: "Taro Yamada",
age: 30,
gender: "male"
}
// typeofでobjの型が取れる
type SampleType1 = typeof hoge
// これと同じ
type SampleType1 = {
name: string;
age: number;
gender: string;
}
// そのobjの型のkeyを取り出してユニオン型を生成 "name" | "age" | "gender"
type SampleType2 = keyof typeof hoge
// objのvalueをユニオン型にする場合 as constが必要
const objData = {
groupName: 'グループ名',
storeName: '店舗名',
address: '住所',
} as const
// "グループ名" | "店舗名" | "住所" のユニオン型ができる
type ObjDataValues = typeof objData[keyof typeof objData]