TypeScript の Record 型を使いこなす

目次
Record<K, V> は「キーが K・値が V のオブジェクト」を表すユーティリティ型です。インデックスシグネチャより簡潔に書け、キーをユニオンに絞ると網羅性チェックまで効きます。
Record とは
Record<Keys, Value> で、指定したキー集合と値の型を持つオブジェクト型を作れます。
type Scores = Record<string, number>;
const scores: Scores = {
math: 80,
english: 92,
};これは { [key: string]: number } とほぼ同じ意味です。
インデックスシグネチャとの違い
任意の文字列キーなら両者はほぼ同等ですが、キーをユニオンに絞れるのが Record の強みです。
// インデックスシグネチャ: キーは string 全般
type A = { [key: string]: number };
// Record: キーを具体的なユニオンに限定できる
type B = Record<"x" | "y", number>; // { x: number; y: number }「決まったキーだけを持つ」ことを表したいなら Record が簡潔です。
ユニオンをキーにすると網羅性が効く
キーにユニオン型を渡すと、キーの抜け漏れがコンパイルエラーになります。
type Role = "admin" | "editor" | "viewer";
const permissions: Record<Role, string[]> = {
admin: ["read", "write", "delete"],
editor: ["read", "write"],
viewer: ["read"],
};viewer を書き忘れるとエラーになるため、ロールが増えたときの対応漏れを防げます。
satisfies と組み合わせる
「値の形は検査したいが、各値の具体型も保ちたい」場合は satisfies と併用します。
const theme = {
primary: "#ea580c",
bg: "#09090b",
} satisfies Record<string, string>;
theme.primary.toUpperCase(); // OK: primary は string と推論されたまま型注釈 : Record<string, string> だと推論が広がりますが、satisfies なら適合検査と具体的な推論を両取りできます。
落とし穴: 存在しないキーへのアクセス
Record<string, V> は「どんなキーでも V が返る」と見なされるため、実際には存在しないキーでも型エラーになりません。
const scores: Record<string, number> = { math: 80 };
scores.science.toFixed(); // 型上は number。だが実行時は undefinedこれを防ぐには tsconfig.json の noUncheckedIndexedAccess を有効にします。すると戻り値が number | undefined になり、アクセス前のチェックを強制できます。
{
"compilerOptions": {
"noUncheckedIndexedAccess": true
}
}まとめ
Record<K, V> は、決まったキーを持つオブジェクトを簡潔に型付けできるユーティリティ型です。ユニオンキーで網羅性を、satisfies で推論の保持を、noUncheckedIndexedAccess で安全性を補えます。インデックスシグネチャを書く前に、まず Record で表せないか検討してみてください。

