Record型とは?「使い方」や「インデックス型との違い」を解説【TypeScript】

TypeScriptでオブジェクトの型を定義していると、

  • キーと値の型をまとめて指定したい
  • 決まったキーを必ず持つオブジェクトを表現したい

と感じることはありませんか?そんなときに役立つのがRecord型です。

この記事では、Record型の基本的な使い方、インデックス型との違い、実際の利用シーンを初心者の方にもわかりやすく解説します。

Record型とは?

Record型はTypeScriptに組み込まれているユーティリティ型のひとつです。「キーの型」と「値の型」を指定することで、その条件に合ったオブジェクト型を自動で作ってくれます。

一言でいうと「キーと値の型を指定して、オブジェクトの型を作るための型」です。

まずは、シンプルなサンプルコードを見てみましょう。

type ScoreBySubject = Record<string, number>;

const scores: ScoreBySubject = {
  math: 90,
  english: 85,
  science: 92,
};

このScoreBySubject型の意味は次のとおりです。

  • キー:string
  • 値:number

つまり、「キーが文字列で、値が数値のオブジェクト」を表す型になります。

Record型の基本構文

Record型は、次のような形で使います。

基本構文

Record<Keys, Type>
  • Keys
    • オブジェクトのキーの型
  • Type
    • 各キーに対応する値の型

Keysには、次の型(またはそのリテラル型)だけを指定できます。

  • string
  • number
  • symbol

Record型とインデックス型の違い

ここまでを見ると、「Record型はインデックス型とほとんど同じでは?」と感じるかもしれません。実際、先ほど示したサンプルコードはインデックス型でも以下のように書けます。

type ScoreBySubject = {
  [key: string]: number;
};

const scores: ScoreBySubject = {
  math: 90,
  english: 85,
  science: 92,
};

この段階では、Record型とインデックス型に大きな違いはなさそうに見えます。

しかし、Record型とインデックス型の大きな違いがあります。それは、Record型はキーを「特定の文字列(リテラル)」で厳密に指定できる点です。次の例を見てみましょう。

type Person = Record<"familyName" | "givenName" | "nickname", string>;

const person: Person = {
  familyName: "Tanaka",
  givenName: "Taro",
  nickname: "Taro-san",
};

この場合、Person型は次の条件を持ちます。

  • 必ずfamilyNamegivenNamenicknameという3つのプロパティを持つ。
  • 各キーに対応する値はすべてstringである。

つまり、Record型は「決まったキーを必ず持つオブジェクト」を型として表現できます。

インデックス型でこのような定義をしたい場合、以下のようにエラーとなってしまいます。

Record型とインデックス型の違い

そのため、インデックス型で書く場合には、次のようになります。

type Person = {
  [key: string]: string;
};

const person: Person = {
  familyName: "Tanaka",
  givenName: "Taro",
  nickname: "Taro-san",
};

この場合、familyNamegivenNamenicknameが必須であるという制約は表現できません。

Record型の内部

実は、Record型は内部的にMapped Typesを使って定義されています。

type Record<K extends keyof any, T> = {
  [P in K]: T;
};

このコードの意味を簡単に説明すると、

  • Kで受け取ったキーの集合を
  • P in Kで1つずつ取り出し
  • それぞれにT型の値を割り当てる

という仕組みです。つまり、「指定されたキーをすべて持つオブジェクト型を作る」ための型というわけです。

Record型の使い道

使い道としては、入れ子になったオブジェクトの型を簡潔に書けるというものが挙げられます。例えば、次のような構造を考えてみます。

type Account = {
  email: string;
  isActive: boolean;
};

type Accounts = {
  admin: Account;
  editor: Account;
  viewer: Account;
};

このように同じ型を何度も書くのは、少し冗長です。Record型を使うと、以下のように書くことができます。

type Account = {
  email: string;
  isActive: boolean;
};

type Role = "admin" | "editor" | "viewer";

type Accounts = Record<Role, Account>;

これで、Accounts型は

  • "admin" | "editor" | "viewer"のキーを必ず持つ
  • 各値はすべてAccount

というオブジェクト型をシンプルに定義することができます。

本記事のまとめ

この記事では『Record型』について説明しました。

Record型は「キーの型」と「値の型」を指定するだけで、条件に合ったオブジェクト型を簡潔に定義できる便利なユーティリティ型です。

特に、以下のような場合にRecord型は大きな力を発揮します。

  • 決まったキーを必ず持つオブジェクトを表現したいとき
  • 同じ型の値を持つオブジェクトをシンプルに書きたいとき

ぜひ実際のコードでRecord型を活用してみてください。

お読みいただきありがとうございました。

スポンサーリンク