TypeScriptには便利なユーティリティ型が数多く存在します。その中でもRecord<K, T>
型は、オブジェクトのキーと値の型を厳密に定義できる強力な型です。本記事では、Record
型の基本から、具体的な使い道、注意点、応用例まで詳しく解説します。
Record型とは?
基本の定義
Record<K, T>
型は、キーK
の型と値T
の型を指定し、それらを持つオブジェクトを作成するための型です。
type Record<K extends keyof any, T> = {
[P in K]: T;
};
つまり、Record<K, T>
は次のようなオブジェクトを表します。
- K:オブジェクトのキーの型(
string | number | symbol
のいずれか) - T:キーに対応する値の型
基本的な使い方
例えば、ユーザーIDをキーとし、ユーザー名を値とするオブジェクトを作成する場合、Record
型を次のように定義できます。
type UserRecord = Record<number, string>;
const users: UserRecord = {
1: "Alice",
2: "Bob",
3: "Charlie"
};
インデックス型との違い
TypeScriptにはRecord<K, T>
型のほかにも「インデックス型」と呼ばれるものがあります。インデックス型とRecord
型の違いを理解することは重要です。
インデックス型とは?
インデックス型は、任意のキーを持つオブジェクトの型を定義する方法です。
type IndexType = {
[key: string]: number;
};
const data: IndexType = {
apple: 10,
banana: 20,
cherry: 30,
};
Record型との違い
型 | キーの制限 | 値の制限 | 用途 |
---|---|---|---|
Record<K, T> | K は指定した型のみ | T は指定した型のみ | 事前に決まったキーを持つオブジェクトの型定義 |
インデックス型 | 任意のキー (string やnumber ) | 指定した型の値 | 任意のキーを持つオブジェクトの型定義 |
例えば、Record<"apple" | "banana", number>
はapple
とbanana
のキーしか持てませんが、インデックス型なら他のキーも追加可能です。
// Record型
const recordData: Record<"apple" | "banana", number> = {
apple: 10,
banana: 20,
// cherry: 30, // エラー!
};
// インデックス型
const indexData: { [key: string]: number } = {
apple: 10,
banana: 20,
cherry: 30, // OK
};
Record型の使い道
オブジェクトのマッピング
Record<K, T>
は、ユニオン型のキーを持つオブジェクトを型安全に定義できます。
type Role = "admin" | "editor" | "viewer";
const permissions: Record<Role, boolean> = {
admin: true,
editor: true,
viewer: false,
};
オブジェクトの形状を制限する
オブジェクトのキーを限定し、値の型を保証する用途にも利用できます。
type Config = Record<"theme" | "language", string>;
const settings: Config = {
theme: "dark",
language: "en",
};
動的なキーのマッピング
キーが動的に決まる場合にもRecord
型を活用できます。
type UserID = number;
type UserInfo = { name: string; age: number };
const users: Record<UserID, UserInfo> = {
1: { name: "Alice", age: 25 },
2: { name: "Bob", age: 30 },
};
Record型の内部
Record<Keys, Type>の型引数
Keys
Record<K, T>
のK
は、オブジェクトのキーの型を表します。string | number | symbol
のいずれかである必要があります。
type KeysExample = Record<"id" | "name", string>;
Type
Record<K, T>
のT
は、キーに対する値の型を表します。- プリミティブ型、オブジェクト型、配列型など、あらゆる型を指定できます。
type ValuesExample = Record<string, { id: number; name: string }>;
Record型の応用例
Record型を関数の引数として使用
Record
型を引数に持つ関数を定義できます。
function printUsers(users: Record<number, string>) {
for (const id in users) {
console.log(`ID: ${id}, Name: ${users[id]}`);
}
}
const users = { 1: "Alice", 2: "Bob" };
printUsers(users);
Record型とマッピング型の組み合わせ
マッピング型と組み合わせることで、より柔軟な型を作成できます。
type UserRoles = "admin" | "user";
type UserPermissions = Record<UserRoles, boolean>;
const permissions: UserPermissions = {
admin: true,
user: false,
};
注意点と対処法
型安全性の限界
Record<K, T>
型は、オブジェクトのキーが固定されるため、動的に追加・削除する際の型安全性が低下する場合があります。
const data: Record<string, number> = {};
data["newKey"] = 123; // OK(ただし、型チェックは緩くなる)
undefinedリスクに注意
オブジェクトのキーが確実に存在する保証はないため、undefined
になるリスクがあります。
const users: Record<number, string> = { 1: "Alice", 2: "Bob" };
console.log(users
); // undefined(キーが存在しない)
対策: Optional Chaining
やin
演算子を使う。
if (3 in users) {
console.log(users
);
}
まとめ
Record<K, T>
型は、オブジェクトのキーと値の型を厳密に定義できる便利な型。- インデックス型とは異なり、キーを特定の型に制限できる。
- 設定値、マッピング、動的なキーの管理など幅広い場面で役立つ。
- ただし、
undefined
のリスクや動的なキーの追加には注意が必要。