TypeScriptで柔軟なオブジェクトの型を定義する場合、「インデックスシグネチャ」はとても便利です。しかし、使い方を誤ると型の安全性を失う可能性もあります。本記事では、インデックスシグネチャの基本から実用的な使い方、注意点、解決策までを網羅的に解説します。

TypeScriptを学び始めた方や、型安全なプログラムを書きたい方は、ぜひ最後まで読んでみてください!

TypeScriptにおけるインデックスシグネチャとは?

インデックスシグネチャは、任意のキーとその値の型を定義する仕組みです。型定義の際に特定のキーを予測できない場合や、動的にプロパティを追加する場合に役立ちます。

インデックスシグネチャの基本構文

TypeScriptでは、以下のようにインデックスシグネチャを定義します。

interface Example {
  [key: string]: number; // 任意のキーが文字列で、値が数値
}

const obj: Example = { a: 1, b: 2 }; // OK
obj["c"] = 3; // OK
  • [key: string]:任意のキーが文字列であることを示しています。
  • number:キーに対応する値の型を指定します。

インデックスシグネチャの特徴と注意点

  1. キーの型はstringまたはnumberのみ
    • TypeScriptでは、キーとしてstring型またはnumber型のみ許可されます。
  2. シグネチャの重複は不可
    • 同じ型のインデックスシグネチャを複数定義することはできません。
  3. 値の型を統一する必要がある
    • インデックスシグネチャの値は一貫性を持たなければなりません。
interface InvalidExample {
  [key: boolean]: string; // Error: キーにbooleanは使用不可
}

インデックスシグネチャの実用例10選

基本的なインデックスシグネチャの使用法

任意のプロパティを許容するオブジェクト型を定義します。

interface UserProfile {
  [key: string]: string;
}

const user: UserProfile = { name: "John", country: "Japan" };

数値型のキーを持つオブジェクト

number型のキーを定義する場合の例です。

interface Scores {
  [key: number]: string;
}

const scores: Scores = { 1: "A", 2: "B" };

関数の引数でのインデックスシグネチャ

動的にキーが追加されるオブジェクトを関数で扱う場合です。

function logKeys(obj: { [key: string]: string }) {
  Object.keys(obj).forEach((key) => console.log(key));
}

logKeys({ name: "Alice", city: "Tokyo" });

クラスにインデックスシグネチャを適用

クラス内でもインデックスシグネチャを活用できます。

class Example {
  [key: string]: string;
}

const obj = new Example();
obj["key1"] = "value1";

Optionalなインデックスシグネチャ

プロパティが必須ではない場合、undefinedも含めて型定義します。

interface OptionalExample {
  [key: string]: string | undefined;
}

Record<K, T>を用いたインデックス型

Record型を使うと、インデックスシグネチャをよりシンプルに表現できます。

type RecordExample = Record<string, number>;

const obj: RecordExample = { a: 1, b: 2 };

ネストされたインデックスシグネチャ

オブジェクトが多階層になる場合の定義例です。

interface Nested {
  [key: string]: { [key: string]: number };
}

const nestedObj: Nested = { level1: { level2: 100 } };

型互換性の解決策: スプレッド構文を使用

スプレッド構文で型を拡張する方法です。

type Base = { [key: string]: number };
type Extended = Base & { additionalKey: string };

固定キーとインデックスシグネチャの組み合わせ

特定のプロパティを固定しつつ、任意のキーも許可します。

interface FixedAndIndex {
  id: number;
  [key: string]: string | number;
}

Readonlyとの組み合わせ

読み取り専用のインデックスシグネチャです。

interface ReadonlyExample {
  readonly [key: string]: string;
}

注意点と解決策

存在しないプロパティへのアクセス対策

noUncheckedIndexedAccessオプションを有効にすると、存在しないプロパティへのアクセスに対して型エラーが出ます。

// tsconfig.json
{
  "compilerOptions": {
    "noUncheckedIndexedAccess": true
  }
}

まとめ

TypeScriptのインデックスシグネチャは柔軟な型定義を可能にしますが、注意点もあります。本記事で紹介した10の実用例や解決策を活用し、安全で型が整ったコードを書いていきましょう!