TypeScriptのRecord型を、自分のキーで理解する
TypeScriptのRecord型
TypeScriptのRecord<K, V>:型付きの参照用オブジェクトを解説 | We.Inc
自分のキーからRecord<K, V>型を作り、それが表すオブジェクトを確認できます。Recordがインデックスシグネチャやマップより適している場面と、検出できるエラーを解説します。
Record<K, V>は組み込みのユーティリティ型で、キーがKから取られ、値がすべてV型であるオブジェクトを表します。文字列リテラルのユニオンを渡すと、コンパイラーは列挙したすべてのキーが存在することを要求します。単なるstringを渡すと、インデックスシグネチャと同じく、開いた辞書のように振る舞います。
Recordの概要
- Record<"a" | "b", number>
- { a: number; b: number }
- いいえ、両方のキーが必須
- Record<string, number>
- { [key: string]: number }
- はい、任意の文字列キー
- Partial<Record<"a" | "b", number>>
- { a?: number; b?: number }
- はい、各キーは省略可能
- Record<number, string>
- { [key: number]: string }
- はい、任意の数値キー
使いたいキーを入力し、値の型を選んでください。ビルダーがRecord型、それが展開される通常のオブジェクト型、キーごとに1行ずつの雛形オブジェクトを書き出します。コードに貼り付ける前に、コンパイラーが何を求めるのかを正確に確認できます。
- キーをカンマ区切りで入力します。例:home, about, contact
- 各キーが持つ値の型を選びます。
- 省略できるキーがある場合は「省略可能」にチェックを入れます。型がPartialで囲まれます。
- 型と雛形オブジェクトをファイルにコピーし、値を埋めます。
- キーのユニオンは手元のデータから導き出しましょう。keyof typeof someObjectや、as const配列に対する(typeof list)[number]を使えば、Recordが元データと常に一致します。
- Record<string, T>は、キーが本当に実行時に決まる場合だけ使いましょう。閉じたユニオンなら、開いた辞書では見逃すプロパティ名の誤字を検出できます。
- 計算されたキーでRecord<string, T>から読み取るなら、noUncheckedIndexedAccessを有効にしましょう。読み取りの結果がT | undefinedになり、実行時の挙動と一致します。
- Record<string, any>は避けましょう。Record<string, unknown>でもどんなオブジェクトも受け付けますが、値を使う前にチェックを強制できます。
よくある質問
Recordはインデックスシグネチャと同じですか?
stringのような広いキーなら同じです。Record<string, number>と{ [key: string]: number }は同じ形を表します。違いが出るのはリテラルのキーです。インデックスシグネチャは特定の名前の存在を要求できませんが、Record<"a" | "b", number>はaとbの両方を必須にします。
RecordではなくMapを使うべきなのはどんなときですか?
キーの追加と削除が頻繁なとき、キーが文字列でないとき、sizeで件数が必要なときはMapを使いましょう。決まった名前の集合、設定オブジェクト、JSON.stringifyに通すものにはRecordを使います。
Recordにプロパティがないと TypeScript に言われるのはなぜですか?
キーの型がユニオンで、オブジェクトがその一部を欠いているからです。tscは、たとえばProperty 'about' is missing in type '{ home: string; }'と報告します。キーを追加するか、省略が正しいならPartialで型を囲みましょう。
Recordは実行時にも存在しますか?
いいえ。型だけの存在で、コンパイル時に消えます。作成したオブジェクトは普通のJavaScriptオブジェクトなので、サーバーのレスポンスにキーが余分にあったり欠けていたりしても、コンパイラーは防げません。
RecordはどのTypeScriptバージョンで追加されましたか?
RecordはPartial、Readonly、Pickとともに TypeScript 2.1 で登場したため、現行のどのコンパイラーにもあります。
Recordの仕組み
- Recordはマップ型です。キーの型の各メンバーをたどり、それぞれに同じ値の型を与えます。だからRecord<"a" | "b", number>は、2つの必須のnumberプロパティを持つオブジェクト型に展開されます。
- 型にすぎないので、プロパティキーになり得るものなら何でも渡せます。文字列リテラル、number、string、symbol、またはそれらのユニオンです。keyof Userのように別の型から作ったキーの型は、Userが変わると自動で更新されます。
閉じたキーと開いたキー
- Recordの価値は閉じた形にあります。ルート名、ステータスのラベル、テーマカラーの参照表をリテラルのユニオンで型付けすれば、誰かがラベルなしで新しいステータスを追加した日にビルドが失敗します。まさに知りたいタイミングです。
- 開いた形のRecord<string, T>は辞書を表します。どのキーが存在するかについては何も語らないため、キーがなくても読み取りの結果はTになります。その差が問題になる場合は、noUncheckedIndexedAccessやMapと組み合わせましょう。
Record、インデックスシグネチャ、Map
- 固定の参照にはリテラルのキーを持つRecord、JSON形式の辞書にはRecord<string, T>かインデックスシグネチャ、プログラムの実行中に変化するコレクションにはMapを選びましょう。最後のケースは、このサイトのTypeScript Mapガイドで詳しく解説しています。
よく使うパターン
as const配列からキーを作る
配列が唯一の情報源になります。ステータスを追加すると、ラベルも追加するまでコンパイラーがラベル表を指摘します。
別の型のフィールドごとに1つのフラグ
keyofで既存の型をキーのユニオンに変えるため、フォームのtouched状態のオブジェクトは常にフォームと同じフィールドを列挙します。
開いた辞書を安全に読む
noUncheckedIndexedAccessを有効にすると、計算されたキーでの読み取りはnumber | undefinedになるため、フォールバックを忘れずに書くことになります。
Record型ビルダー
キー(カンマ区切り)
値の型
キーを省略可能にする(Partialで囲む)
型
展開後の型
雛形オブジェクト
キーを1つ以上入力してください。キーは英字、_、$のいずれかで始める必要があります。
無料で始める · クレジットカード不要
関連: ガイド|Webマーケティングと集客を学ぶ
製品
ご利用いただける方
機能
リソース
会社情報
サイトマップを見る