TypeScriptのコードでたまにみかけるtype HogeType = typeof Hoge[kyof typeof Hoge]
の意味について解説をします。
目次
結論: typeof X[kyeof typeof X]の意味
typeof X[kyeof typeof X]
はオブジェクトXのプロパティの型をUnion型で表現します。
具体例は以下の通りです。
const Color = {
Red: "red",
Green: "green",
Blue: "blue",
} as const;
type Color = typeof Color[keyof typeof Color];
// 以下のように評価される
// type Color = "red" | "green" | "blue"
特に、Enum型の代わりにUnion型を利用する際にtypeof X[kyeof typeof X]
の表現がよく使われます。
Enum型とUnion型の比較は【TypeScript】Enum型からUnion型に書き換える方法と具体例で紹介しています。
typeof X[kyeof typeof X]で利用される演算子や型について
typeof X[kyeof typeof X]
を使う場面では以下の演算子や型が利用されます。
- const assertion
- keyof
- typeof
- Lookup Types(ルックアップ型)
以下ではそれぞれについて解説をします。
const asssertion(constアサーション)について
const asssertion(constアサーション)とはオブジェクトや値をreadonly(再代入不可)にする機能です。
const assertionはreadonlyにしたい変数の末尾にas const
をつけることで実現できます。
const asssertionの具体例は以下の通りです。
let arr = [10, 20] as const;
// 以下のように型推論される
// let arr: readonly [10, 20]
let obj = { text: "Hello" } as const;
// 以下のように型推論される
// let obj: {
// readonly text: "Hello";
// }
// ネストしたオブジェクトの場合、再帰的にreadonlyが付与される
let taro = {
firstName: 'Taro',
lastName: 'Suzuki',
birthplace: {
country: 'Japan',
prefecture: 'Tokyo'
}
} as const
// 以下のように型推論される
// let taro: {
// readonly firstName: "Taro";
// readonly lastName: "Suzuki";
// readonly birthplace: {
// readonly country: "Japan";
// readonly prefecture: "Tokyo";
// };
// }
const assertionの利用した場合としない場合の比較は以下の通りです。
// const assertionを利用した場合
const x = { a: 1 } as const;
x.a = 3 // error
// const assertionを利用しない場合
const x = { a: 1 };
x.a = 3
console.log(x.a) // 3
keyof(index type query operator)について
keyof
(index type query operator)はオブジェクトの型を受け取り、オブジェクトのプロパティで構成されたUnion型を返す演算子です。
keyof
の具体例は以下の通りです。
type Person = {
firstName: string;
lastName: string;
};
type PersonPropertyType = keyof Person;
// 'firstName' | 'lastName'
// OK
const firstName: PersonPropertyType = "firstName";
// OK
const lastName: PersonPropertyType = "lastName";
// NG
const age: PersonPropertyType = "age";
// Type '"age"' is not assignable to type 'key
typeofについて
typeof
はオペランド(被演算子、演算対象の変数のこと)の型を文字列として返す演算子です。typeof 変数
を型にすると、変数の型定義が利用できます。
typeof
の具体例は以下の通りです。
// プリミティブ型の場合
const hello: string = "Hello";
console.log(typeof hello);
// string
type TypeOfHello = typeof hello;
// 以下のように型推論される
// type TypeOfHello = string
// OK
const hi: TypeOfHello = "Hi";
// オブジェクトの場合
const taro = {
name: "Taro",
age: 20,
};
console.log(typeof taro);
// object
type TypeofTaro = typeof taro;
// 以下のように型推論される
// type TypeofTaro = {
// name: string;
// age: number;
// }
// OK
const jiro: TypeofTaro = {
name: "Jiro",
age: 22,
};
Lookup Types(ルックアップ型)について
Lookup Types(ルックアップ型)とはオブジェクトのプロパティの型を表現するものです。
Lookup Typesは『indexed access operator』とも呼ばれています。1
Lookup TypesはT[K]
のように記述します。T[K]
は『型Tにおけるキー(プロパティ)Kの型』を表現します。
Lookup Typesの具体例は以下の通りです。
type Person = {
firstName: string;
lastName: string;
};
// Person型のfirstNameキーの型
type FirstNameType = Person["firstName"];
// 以下のように型推論される
// type FirstNameType = string
// Person型のlastNameキーの型
type LastNameType = Person["lastName"];
// 以下のように型推論される
// type LastNameType = string
typeof X[kyeof typeof X]を要素分解することで意味を理解する
以下のサンプルコードのtypeof Color[keyof typeof Color]
は"red" | "green" | "blue"
というUnion型になります。
const Color = {
Red: "red",
Green: "green",
Blue: "blue",
} as const;
type Color = typeof Color[keyof typeof Color];
// 以下のように評価される
// type Color = "red" | "green" | "blue"
typeof Color[keyof typeof Color]
の意味について、以下の手順で説明します。
- 『typeof X』について
- 『keyof typeof X』について
- 『typeof X[keyof typeof X]』について
typeof X: オブジェクトを型として定義する
typeof Color
はColorオブジェクトを型として定義します。
const Color = {
Red: "red",
Green: "green",
Blue: "blue",
} as const;
// 以下のように評価される
// const Color: {
// readonly Red: "red";
// readonly Green: "green";
// readonly Blue: "blue";
// }
type TypeOfColor = typeof Color
// 以下のように評価される
// type TypeOfColor = {
// readonly Red: "red";
// readonly Green: "green";
// readonly Blue: "blue";
// }
keyof typeof X: プロパティをUnion型で表現する
keyof typeof Color
はtypeof Color
、つまりColor型のプロパティをUnion型で定義します。
const Color = {
Red: "red",
Green: "green",
Blue: "blue",
} as const;
type TypeOfColor = typeof Color
// 以下のように評価される
// type TypeOfColor = {
// readonly Red: "red";
// readonly Green: "green";
// readonly Blue: "blue";
// }
type KeyofTypeofColor= keyof typeof Color
// 以下のように評価される
// type KeyofTypeofColor = "Red" | "Green" | "Blue"
typeof X[keyof typeof X]: プロパティの型をUnion型で表現する
typeof Color
はColor
型を意味します。
ですので、typeof Color[keyof typeof Color]
はColor
型のkeyof typeof Color
、つまり"Red" | "Green" | "Blue"
プロパティの型を取得します。
const Color = {
Red: "red",
Green: "green",
Blue: "blue",
} as const;
type KeyofTypeofColor= keyof typeof Color
// 以下のように評価される
// type KeyofTypeofColor = "Red" | "Green" | "Blue"
type Color = typeof Color[keyof typeof Color]
// 以下のように評価される
// type Color = "red" | "green" | "blue"
以上の結果、typeof Color[keyof typeof Color]
は"red" | "green" | "blue"
というUnion型として推論されます。
typeof X[kyeof typeof X]を利用した具体例
showColor
は引数のColor型の値に応じて動的なログを出力するメソッドです。
const Color = {
Red: "red",
Green: "green",
Blue: "blue",
} as const;
type Color = typeof Color[keyof typeof Color];
// type Color = "red" | "green" | "blue"
const showColor = (color: Color) => {
if (color === Color.Red) {
console.log("赤色です");
}
if (color === Color.Green) {
console.log("緑色です");
}
if (color === Color.Blue) {
console.log("青色です");
}
};
showColor(Color.Red);
// 赤色です
showColor(Color.Green);
// 緑色です
showColor(Color.Blue);
// 青色です
// OK
// 文字列リテラルを直接指定してもOK
showColor("red");
// 赤色です
// NG
// Union型に含まれてない
showColor("yellow");
// => Argument of type '"yellow"' is not assignable to parameter of type 'Color'.(2345)
参考: const assertionの命名ルール(コーディング規約)に関する個人的な考え
as const
を付与した変数や、変数のプロパティ(メンバ)の命名ルールは人によって様々です。
例えば、constアサーション「as const」 (const assertion)では変数および変数のメンバはキャメルケース(camelCase)で定義していますし、TypeScript 3.4では変数名はパスカルケース(PascalCase)、メンバはキャメルケース(camelCase)で定義しています。
ですのでas const
に関する明確な命名ルールはありませんが、typeof X[keyof typeof X]
を利用するシチュエーションにおいてはenumをUnion型に置き換える目的でas const
が利用されているため、enumと同じ命名ルールに従った方がよいというのが個人的な考えです。
スタイルガイド(コーディング規約)には「enum名・enumメンバともにパスカルケース(PascalCase)にする」という命名ルールが存在するため、本記事のas const
が付与された変数名およびプロパティにも同様の命名ルールを適用しています。
まとめ
- typeof X[kyeof typeof X]はオブジェクトXのプロパティの型をUnion型で表現する
- as constはオブジェクトや値をreadonlyにする場合に利用する
- keyofはオブジェクトの型を受け取り、オブジェクトのプロパティで構成されたUnion型を返す
- typeofはオペランドの型を文字列として返す
- T[K]は型Tにおけるキー(プロパティ)Kの型を表現する
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!