はじめに
皆さんこんにちは。新卒2年目エンジニアの細川です。
今回はTypeScriptのRecord型についてまとめてみようと思います。
Record型とは?
Record型はTypeScriptの組み込みの型で、プロパティのKeyの型と中身の型を渡すことで、その型に対応したオブジェクトの型を生成してくれるものになります。
例を見てみましょう。
type StringNumber = Record<string, number>;
const value: StringNumber = { a: 1, b: 2, c: 3 };
この場合、各プロパティのキーがstringで中身がnumberのobjectとなっています。
Record<Keys, Type>
のように、第一引数にKeyの型、第二引数にその中身の型を渡すという使い方をします。
Keysには、string, number, symbolとそれぞれのリテラル型しか渡せないようです。
これだけでは、インデックス型と大差ないように見えます。インデックス型で実装してみると以下のようになります。
type StringNumber = {[key: string]: number};
const value: StringNumber = { a: 1, b: 2, c: 3 };
インデックス型について気になる方はこちらを確認してみてください。
インデックス型との違い
Record型はリテラルで型を指定できますが、インデックス型はリテラルで指定できないという違いがあります。
以下のようなコードを考えてみます。
type Person = Record<"firstName" | "middleName" | "lastName", string>;
const person: Person = {
firstName: "Robert",
middleName: "Cecil",
lastName: "Martin",
};
この場合、Person型は”firstName”, “middleName”, “lastName”のフィールドを持つオブジェクトとなります。
このような定義をしたい場合、インデックス型では、以下のようにエラーとなってしまいます。
Record型の使い道
使い道としては、入れ子になっているobjectの定義を簡潔に記述できるというものが挙げられます。
以下のように各プロパティがUser型のUsersというObjectを作りたい場合を考えてみます。
type User = {
name: string
age: number
}
type Users = {
user1: User
user2: User
user3: User
user4: User
}
この場合、Record型を使うことで、型定義を簡潔に記述することができます。
type User = {
name: string
age: number
}
type UsersKeys = "user1" | "user2" | "user3" | "user4"
type Users = Record<UsersKeys, User>
他の使い道としては、こちらのページにわかりやすくまとめてくださっていたので、参考にしてみてください。
Record型の内部
Record型の実装としては以下のようにMapped Typesになっています。
type Record<K extends keyof any, T> = {
[P in K]: T;
};
Mapped Typesについては別の記事でまとめるつもりですが、Kとして受け取った型を、キーの制約として利用するような型になっています。
とりあえず気になる方はこちらを参考にしてみて下さい!
まとめ
- Record型はKeyの型と、中身の型を渡すとそれに対応したObjectの型を作ってくれる
- Objectの入れ子になっているような型の定義をスマートに書ける!
- Mapped Typesの応用である
おわりに
今回はTypeScriptのRecord型についてまとめてみました。
組み込み型の中にはまだまだ知らないものがたくさんあるので、今後も少しずつまとめていきたいと思います。
他にもこちらのようにTypeScriptのあれこれをまとめています。少しでも皆さんの参考になれば幸いです。