TypeScriptの「型」の深淵:InterfaceのインデックスシグネチャとRecord型の決定的な違い
こんにちは。TypeScriptのコードを書き進める中で、ふと「これ、本当に型安全なのだろうか?」と不安になったことはありませんか?
特に「オブジェクトのキーが動的に決まる場合」の扱い。多くの人が最初にぶつかる壁であり、同時にTypeScriptの型システムの厳格さを理解する重要な分岐点でもあります。
今回は、Interfaceで古くから使われている「インデックスシグネチャ」と、現代的な「Record型」の決定的な違いについて、コンパイラの裏側を覗くような視点で紐解いていきましょう。ここをクリアすれば、あなたの型定義は一段階上のレベルへ到達しますよ。
—
1. そもそも「インデックスシグネチャ」とは何か?
インデックスシグネチャは、オブジェクトが「どんなキー名であっても、特定の型の値を持つ」ことを保証する仕組みです。
interface UserScores {
// 「キーは文字列で、値は数値である」と定義
[key: string]: number;
}
const scores: UserScores = {
math: 90,
english: 85,
};
// コンパイルは通るが…?
console.log(scores.science); // undefined が返ってくる!
なぜこれが「危険」なのか?
上記の例で `scores.science` にアクセスしたとき、TypeScriptは「あ、これは `number` 型だね」と判定します。しかし、実行時には `undefined` が返ってきてしまいます。
TypeScriptは「キーさえ文字列なら値は必ずあるはずだ」と信じ込んでしまいますが、現実は甘くない。存在しないキーにアクセスしても、型システム上は「数値」として扱われてしまい、後続の計算で予期せぬバグ(`NaN`の発生など)を引き起こす原因になります。
—
2. 救世主 `Record` の登場
そこで登場するのが、TypeScript標準のユーティリティ型である `Record
具体的な使い方の比較
// インデックスシグネチャの場合
interface RawData {
[key: string]: number;
}
// Record型の場合(キーを明示的に制限する)
type Subject = ‘math’ | ‘english’ | ‘science’;
const scores: Record
math: 90,
english: 85,
science: 95,
};
なぜ `Record` が安全なのか?
`Record` を使うと、コンパイラに対して「どのキーが存在し得るか」を完全に掌握させることができます。もし `science` の入力を忘れたら、コンパイラは即座にエラーを出して教えてくれます。
「なんとなく文字列なら何でもいい」という曖昧な状態(インデックスシグネチャ)から、「このキーセットが正解だ」という厳格な契約(Record)への移行。これが型安全性を飛躍的に高める鍵です。
—
3. なぜ現場ではインデックスシグネチャが嫌われるのか
先ほど触れた通り、インデックスシグネチャの最大の弱点は「値が必ず存在するという嘘をつく」点にあります。
もし、本当にキーが動的で、値が存在しない可能性があるなら、以下のように書くべきです。
interface SafeData {
[key: string]: number | undefined; // 明示的に undefined を許容する
}
const data: SafeData = { math: 100 };
if (data.science !== undefined) {
// ここで初めて「数値である」と確信できる(型ガード)
console.log(data.science + 10);
}
「インデックスシグネチャを使うなら、値の型には `| undefined` を含めるのが礼儀」。これを徹底するだけで、`undefined` に起因するランタイムエラーは劇的に減ります。
—
結論:使い分けの指針
ここまで読んでくれたあなたなら、もう迷うことはありません。
- キーが事前に決まっている(または列挙できる)場合:
迷わず `Record
- キーが完全に予測不能な外部データ(APIのレスポンス等)の場合:
インデックスシグネチャを使いますが、必ず値の型に `| undefined` を含め、利用時には型ガード(存在チェック)を行うこと。
TypeScriptの型システムは、あなたのコードの「意図」をコンパイラに伝えるための言語です。`Record` を使うことは、「私はこのデータの構造を完全に把握している」という宣言に他なりません。
この「厳格さ」こそが、大規模開発を支えるTypeScriptの真髄です。今日から、あなたの型定義に少しだけ「厳しさ」を加えてみてください。それだけで、明日からのデバッグ時間が驚くほど減るはずですよ。
それでは、また次回の深掘りでお会いしましょう!