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

TypeScriptインデックスシグネチャのイラストと実例10選の文字TypeScript
この記事は約27分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

TypeScriptは、JavaScriptに静的型を加えた言語として広く受け入れられてきました。

特に大規模な開発プロジェクトでの利用が増えてきており、TypeScriptの持つ型システムの特徴を生かすことで、コードの品質やメンテナンス性を向上させることができます。

その中でも、インデックスシグネチャはTypeScriptの強力な特徴の1つです。

オブジェクトのプロパティ名を柔軟に扱うことができるこの機能は、多くの実践的なシーンで役立ちます。

この記事では、インデックスシグネチャの基本から、さまざまな使用例、そして注意点やカスタマイズ方法まで、具体的なサンプルコードとともに解説していきます。

特に、10の具体的な実例を中心に、この機能をどのように活用できるのか、詳しく見ていきましょう。

●TypeScriptとは

TypeScriptはMicrosoftが開発しているオープンソースのプログラミング言語です。

JavaScriptのスーパーセットとして位置づけられており、JavaScriptのすべての機能に加え、静的型検査やインターフェース、ジェネリクスなどの強力な型システムの特徴を持ちます。

TypeScriptを使うことで、コードの品質を向上させることができます。

特に大規模なプロジェクトでは、バグの発見やコードのリファクタリングが容易になるなどの利点があります。

○基本的な特徴

TypeScriptの特徴は多岐にわたりますが、主な特徴として次の点が挙げられます。

  1. 静的型検査:変数や関数の引数、戻り値に型を指定することができます。これにより、コンパイル時に型の不整合などのエラーを検出できます。
  2. オプショナルな型注釈:型注釈はオプショナルです。必要な場面でのみ型を指定することができるため、徐々に型を導入することも可能です。
  3. ジェネリクス:高度な型抽象化や型の再利用が可能となり、柔軟なコード設計が行えます。
  4. インターフェース:オブジェクトの形状やクラスの構造を定義することができます。これにより、コードの契約を明確にすることが可能となります。

これらの特徴を活かして、より品質の高いコードを実現することができます。

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

インデックスシグネチャは、オブジェクトのプロパティ名とその型を柔軟に扱うことができる機能です。

具体的には、オブジェクトが持つことのできるプロパティ名の集合と、それに関連する値の型を指定することができます。

○定義と基本的な使い方

インデックスシグネチャの基本的な定義は次のようになります。

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

このコードでは、StringDictionaryというインターフェースを定義しています。

このインターフェースは、文字列のキーを持ち、値も文字列型であるようなオブジェクトを表しています。

例えば、次のようなオブジェクトはこのインターフェースに合致します。

let obj: StringDictionary = {
    name: "Taro",
    city: "Tokyo"
};

この例では、objStringDictionaryインターフェースに従ったオブジェクトとして定義されており、キーと値がともに文字列型であるため問題なく代入できます。

逆に、次のようなオブジェクトはエラーとなります。

let invalidObj: StringDictionary = {
    name: "Taro",
    age: 30
};

このinvalidObjは、ageの値が数値型であるため、StringDictionaryインターフェースの定義に合致しないためエラーが発生します。

●実例10選:インデックスシグネチャの使い方

TypeScriptのインデックスシグネチャは、オブジェクトのキーと値の型を柔軟に定義するための強力なツールです。

ここでは、インデックスシグネチャの使い方について、具体的な実例を交えて詳しく解説します。

○サンプルコード1:基本的なインデックスシグネチャの使用法

このコードでは、文字列のキーと文字列の値を持つオブジェクトの型を定義しています。

また、具体的なオブジェクトの例も表しています。

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

const example: StringDictionary = {
    name: "Taro",
    country: "Japan"
};

この例では、namecountryのような任意の文字列キーに対して、文字列の値を持つことができます。

○サンプルコード2:数値型のキーを持つオブジェクトの定義

このコードでは、数値のキーと文字列の値を持つオブジェクトの型を定義しています。

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

const user: NumberDictionary = {
    1: "Taro",
    2: "Jiro"
};

このオブジェクトでは、数値のキーに対応する文字列の値を持つことができます。

○サンプルコード3:関数の戻り値としての使用

このコードでは、関数の戻り値としてインデックスシグネチャを使用しています。

function getUserRoles(userId: number): {[role: string]: boolean} {
    return {
        admin: true,
        user: false
    };
}

const roles = getUserRoles(1);

この関数は、ユーザーのIDを受け取り、そのユーザーのロールとそのロールの真偽値を持つオブジェクトを返します。

○サンプルコード4:インデックスシグネチャを持つクラスの作成

TypeScriptでのクラス定義においても、インデックスシグネチャを活用することができます。

この実例では、インデックスシグネチャを用いたクラスの作成方法について詳しく解説します。

クラス内でインデックスシグネチャを使用すると、任意のキーと値のペアを持つことが可能となり、柔軟なデータ構造を持つクラスを定義することができます。

このコードでは、Dictionaryというクラスを定義しています。

この例では、文字列型のキーと、任意の型の値を持つことができるインデックスシグネチャを定義しています。

class Dictionary<T> {
    // 文字列キーとジェネリック型Tのインデックスシグネチャを定義
    [key: string]: T;
}

// 例として、Dictionaryクラスのインスタンスを生成
let fruits = new Dictionary<number>();

fruits["apple"] = 150;
fruits["banana"] = 100;
fruits["orange"] = 120;

// フルーツの名前と値段を出力
for (let fruit in fruits) {
    console.log(`${fruit} の値段は ${fruits[fruit]} 円です。`);
}

上記のコードでは、Dictionaryクラスはジェネリック型Tを持っており、このTを用いてインデックスシグネチャを定義しています。

このため、インスタンス化する際に、値として持つデータ型を指定することが可能となっています。

実際にfruitsという変数でDictionaryクラスをインスタンス化して、フルーツの名前をキーとし、その値段を値として格納しています。

その後、for…in文を使って、それぞれのフルーツと値段を出力しています。

この例から、インデックスシグネチャを持つクラスが、どのように動的なキーと値のペアを持つことができるのかがわかるでしょう。

このように、TypeScriptのインデックスシグネチャは、オブジェクトだけでなく、クラスの定義においても非常に有用です。

特に、キーと値のペアが動的で変わる可能性がある場合や、柔軟なデータ構造を持つクラスを作成する際に役立ちます。

このコードを実行すると、次のような出力が得られます。

apple の値段は 150 円です。
banana の値段は 100 円です。
orange の値段は 120 円です。

この出力結果より、キーとして設定したフルーツの名前と、その値として設定した値段が正しく取得できていることが確認できます。

○サンプルコード5:拡張性の高い関数の定義

TypeScriptのインデックスシグネチャを活用することで、拡張性の高い関数も実現することができます。

ここでは、インデックスシグネチャを活用して様々なプロパティを受け取ることのできる関数を作成する方法について詳しく解説します。

// インデックスシグネチャを利用した関数の例
type Config = {
    [key: string]: string | number;
};

function setup(config: Config) {
    // configには様々なプロパティを持つオブジェクトが渡される
    for (const key in config) {
        // ここではkeyとvalueをログに出力
        console.log(`${key}: ${config[key]}`);
    }
}

// 使用例
const configExample = {
    host: "localhost",
    port: 8080,
    database: "sampleDB"
};
setup(configExample);

このコードでは、Configという型を定義し、任意のキーに対して文字列や数値を持つプロパティを持つことができるようにしています。

setup関数はこのConfig型の引数を受け取り、その中のすべてのキーと値をログに出力します。

上記の使用例では、configExampleというオブジェクトを定義し、それをsetup関数に渡しています。

その結果、ログには各キーと値が出力されることとなります。

実際に上記のコードを実行すると、次のような出力がされます。

host: localhost
port: 8080
database: sampleDB

このように、インデックスシグネチャを活用することで、柔軟なキーのオブジェクトを引数として受け取る関数を作成することができます。

これにより、関数の利用シーンを大きく拡張することができるのです。

しかし、この方法にはいくつかの注意点があります。

特に、関数内での型チェックが難しくなること、また、関数の利用者が何のプロパティを設定すれば良いのかが不明瞭になる可能性があるため、適切なコメントやドキュメントをもって利用者に情報を提供することが大切です。

また、インデックスシグネチャを活用しつつも、特定のプロパティの存在を保証することも可能です。

type AdvancedConfig = {
    host: string;
    port: number;
    [key: string]: string | number;
};

function advancedSetup(config: AdvancedConfig) {
    console.log(`接続先ホスト: ${config.host}`);
    console.log(`接続ポート: ${config.port}`);
    // その他のプロパティを出力
    for (const key in config) {
        if (key !== "host" && key !== "port") {
            console.log(`${key}: ${config[key]}`);
        }
    }
}

// 使用例
const advancedConfigExample = {
    host: "localhost",
    port: 8080,
    database: "advancedDB",
    timeout: 6000
};
advancedSetup(advancedConfigExample);

上記のコードでは、AdvancedConfigという型ではhostportが必須のプロパティとして定義されていますが、それ以外の任意のプロパティも持つことができます。

これにより、基本的な設定を保証しつつ、拡張性も持たせることができます。

このコードを実行すると、次のような出力が得られます。

接続先ホスト: localhost
接続ポート: 8080
database: advancedDB
timeout: 6000

このように、TypeScriptのインデックスシグネチャを活用することで、拡張性と型の安全性を両立させた設計を行うことができます。

○サンプルコード6:インデックスシグネチャを用いた条件型

TypeScriptのインデックスシグネチャは、単にオブジェクトのキーと値の型を指定するだけでなく、より高度な型操作にも使用できます。

今回は、インデックスシグネチャを用いて条件型を作成する方法を詳しく解説します。

このコードでは、インデックスシグネチャと条件型を組み合わせて、特定のキーが存在するかどうかを確認し、それに基づいて型を分岐させるコードを表しています。

この例では、オブジェクトが特定のキーを持つ場合と持たない場合で、異なる型を返すようにしています。

// インデックスシグネチャを用いた条件型の定義
type HasKey<T, K extends string> = K in keyof T ? true : false;

// サンプルオブジェクトの定義
const sampleObj = {
    name: "Taro",
    age: 25
};

// 型の確認
type IsNameExists = HasKey<typeof sampleObj, "name">;  // true
type IsAddressExists = HasKey<typeof sampleObj, "address">;  // false

上記のサンプルコードでは、HasKeyという条件型を定義しています。

この型は、与えられたオブジェクトTがキーKを持つかどうかをチェックし、持つ場合はtrue、持たない場合はfalseという型を返します。

そして、sampleObjというオブジェクトを定義し、このオブジェクトがnameキーを持つかどうかをHasKey型を用いて確認しています。

結果として、IsNameExiststrue型、IsAddressExistsfalse型になります。

このようにインデックスシグネチャを活用することで、オブジェクトが持つキーに基づいて型の分岐を行うことが可能になります。

これは、動的なデータ構造を扱う際や、APIのレスポンスなどの型を柔軟にハンドリングするシチュエーションで非常に役立ちます。

応用として、この技術を使ってAPIのレスポンス型を定義する際に、特定のキーが存在する場合と存在しない場合で、それぞれ異なるプロパティを持つ型を定義することも可能です。

例えば、次のようにAPIのレスポンスにerrorキーが存在する場合と、存在しない場合で、異なる型を持つように定義することができます。

type ApiResponse<T> = HasKey<T, "error"> extends true ? { error: string } : { data: T };

// APIのレスポンスのサンプル
type SuccessResponse = { data: { id: number, name: string } };
type ErrorResponse = { error: string };

type CheckSuccess = ApiResponse<SuccessResponse>;  // { data: { id: number, name: string } }
type CheckError = ApiResponse<ErrorResponse>;  // { error: string }

上記のコードでは、ApiResponseという条件型を用いて、レスポンスがerrorキーを持つ場合と持たない場合で、異なる型を持つように定義しています。

これにより、APIのレスポンスをより詳細に型付けすることができ、エラーハンドリングも効率的に行うことが可能になります。

○サンプルコード7:マップ型の拡張

TypeScriptでの型の拡張や変更は非常に柔軟です。

特にマップ型を使用すると、既存の型を基に新しい型を生成することができます。

インデックスシグネチャと併用することでさらなる柔軟性を手に入れることができるのです。

このコードでは、マップ型を使って既存の型を拡張するコードを表しています。

この例では、既存のオブジェクト型から新しいオブジェクト型を生成しています。

// 既存のオブジェクト型の定義
type OriginalType = {
  name: string;
  age: number;
};

// 既存の型を拡張して新しい型を作成
type ExtendedType = {
  [K in keyof OriginalType]: OriginalType[K] | null;
};

// 使用例
const person: ExtendedType = {
  name: "田中",
  age: null
};

console.log(person); // { name: "田中", age: null }

この例のポイントは、ExtendedTypeの定義にあります。

ここでkeyof OriginalTypeを使用して、OriginalTypeのすべてのキーを取得しています。

その後、それらのキー(K)に対する値として、OriginalType[K](オリジナルの型)またはnullを設定しています。

結果、ExtendedTypeOriginalTypeのすべてのキーを維持しつつ、各キーの値の型がnullを許容する型となります。

このコードを実行すると、personオブジェクトの内容がコンソールに表示され、nameには文字列の”田中”が、ageにはnullが格納されていることが確認できます。

このようにマップ型は、既存の型をベースに新しい型を簡単に作成するのに役立ちます。

特に、APIのレスポンスや外部のデータ構造に対応する際などに、柔軟に型をカスタマイズすることが求められるシーンでの活用価値は非常に高いです。

次に、このマップ型の拡張を応用した例を見てみましょう。

// 既存のオブジェクト型
type Product = {
  id: number;
  name: string;
  price: number;
};

// すべてのキーの型をstringに変更
type StringifiedProduct = {
  [K in keyof Product]: string;
};

// 使用例
const stringProduct: StringifiedProduct = {
  id: "1",
  name: "テーブル",
  price: "5000"
};

console.log(stringProduct); // { id: "1", name: "テーブル", price: "5000" }

上記のコードでは、Product型を基に新しいStringifiedProduct型を定義しています。

この新しい型では、すべてのキーの値の型がstringとなっています。

そのため、stringProductオブジェクトでは、全ての値が文字列として格納されていることが確認できます。

この方法を利用すれば、特定の処理や変換の際に、一時的に型を変更する必要がある場面などで、効率的にコードを記述することができます。

○サンプルコード8:ネストしたインデックスシグネチャ

インデックスシグネチャは非常に柔軟性が高く、オブジェクト内のオブジェクト、さらにその中のオブジェクトといったようにネストして使用することが可能です。

ここでは、ネストしたインデックスシグネチャの実装方法とその活用例について解説します。

このコードでは、ネストしたオブジェクト構造に対応するインデックスシグネチャを定義する方法を表しています。

この例では、外部のオブジェクトと内部のオブジェクトの両方にインデックスシグネチャを使用して動的なキーの対応を行っています。

type NestedObjects = {
  [outerKey: string]: {
    [innerKey: string]: number;
  }
}

// サンプルデータ
const data: NestedObjects = {
  Tokyo: {
    Shibuya: 2023154,
    Shinjuku: 3124567,
  },
  Osaka: {
    Umeda: 1547890,
    Namba: 9876543,
  }
}

// 例: TokyoのShibuyaのデータを取得
console.log(data["Tokyo"]["Shibuya"]); // 2023154

このサンプルでは、都市名をキーとし、その都市内のエリア名と人口を組み合わせてネストしたオブジェクトを作成しています。

上記のコードを実行すると、TokyoShibuyaエリアの人口2023154がコンソールに出力されます。

また、ネストしたインデックスシグネチャを利用することで、動的なキーのオブジェクトを効率よく扱うことができます。

例えば、特定のエリアの人口を更新したり、新しい都市やエリアを追加する場合にもスムーズに対応可能です。

// 例: OsakaのNambaエリアの人口を更新
data["Osaka"]["Namba"] = 10000000;

// 新しい都市とエリアを追加
data["Fukuoka"] = {
  Hakata: 7890123,
  Tenjin: 4567890
}

console.log(data["Osaka"]["Namba"]); // 10000000
console.log(data["Fukuoka"]["Hakata"]); // 7890123

このようにネストしたインデックスシグネチャを利用することで、動的なキーのデータを柔軟に扱うことができます。

初心者の方も、これを活用することでTypeScriptのコードの品質や保守性を向上させることが期待されます。

○サンプルコード9:Optionalなインデックスシグネチャ

TypeScriptでは、インデックスシグネチャを用いることで、オブジェクトのキーとして任意の文字列や数字を受け入れることができます。

しかし、すべてのオブジェクトが全てのキーを持っているわけではありません。

このような場面で便利なのが、Optionalなインデックスシグネチャです。

このコードではOptionalなインデックスシグネチャの使い方を紹介しています。

この例では、オブジェクトが必ずしも持っていない可能性のあるキーに対して、タイプを指定する方法を表しています。

interface UserProfiles {
    // キーはstring、値はstringかundefined
    [username: string]?: string;
}

// このオブジェクトはUserProfiles型として有効です
const profiles: UserProfiles = {
    tanaka: "Tanaka Taro",
    // "suzuki"のようなキーが存在しなくてもエラーにならない
};

上記のUserProfiles型は、任意の文字列キーと、そのキーに対応する値としてstringまたはundefinedを取ることができます。

そのため、suzukiのようなキーがprofilesオブジェクトに存在しなくてもエラーが発生しないのです。

この例を元にコードを実行すると、tanakaキーにはTanaka Taroという文字列が格納されていますが、suzukiのようなキーが存在しなくてもエラーは発生しないことがわかります。

注意点としては、Optionalなインデックスシグネチャを使用する場合、該当のキーがオブジェクトに存在しない場合、値としてundefinedが返される点です。

これにより、存在しないキーへのアクセスを安全に行うことができるようになります。

応用例として、存在しないキーにアクセスした際のデフォルト値を設定したい場面が考えられます。

Optionalなインデックスシグネチャと関数を組み合わせて、デフォルトの値を返す方法を表すサンプルコードを紹介します。

function getProfile(profiles: UserProfiles, username: string, defaultValue: string): string {
    return profiles[username] || defaultValue;
}

const profileName = getProfile(profiles, "suzuki", "Unknown User");
console.log(profileName); // "Unknown User"

上記のコードでは、getProfile関数を使ってprofilesオブジェクトからユーザー名を取得しています。

指定したユーザー名が存在しない場合、デフォルトの値Unknown Userが返されます。

○サンプルコード10:Readonlyとの組み合わせ

TypeScriptのインデックスシグネチャを深堀りしてきましたが、最後に、Readonlyとインデックスシグネチャを組み合わせた使い方を解説します。

Readonlyは、オブジェクトのプロパティを変更不可にするユーティリティタイプです。

この特性を活かして、変更不可のデータ構造を作ることができます。

このコードでは、Readonlyとインデックスシグネチャを使用して、文字列のキーと文字列の値を持つ変更不可のオブジェクトを定義する方法を表しています。

この例では、インデックスシグネチャを用いて任意の文字列キーとそのキーに関連する文字列値を持つオブジェクトを作成し、それをReadonlyでラップしています。

// Readonlyとインデックスシグネチャの組み合わせ
type ReadonlyStringDictionary = Readonly<{ [key: string]: string }>;

const dictionary: ReadonlyStringDictionary = {
  hello: "こんにちは",
  world: "世界"
};

// 以下のコードはコンパイルエラーとなる
// dictionary.hello = "やあ";  // Error: オブジェクトは読み取り専用のプロパティであるため、代入することはできません。

上記のサンプルコードで定義したReadonlyStringDictionaryは、文字列のキーと文字列の値を持つオブジェクトで、一度値が設定されるとそれを変更することはできません。

そのため、dictionary.hello = "やあ";というように値を変更しようとすると、コンパイルエラーが発生します。

この機能は、アプリケーション内で不変なデータ構造を持つ必要がある場合や、関数の引数としてオブジェクトを受け取り、そのオブジェクトを変更したくない場合に非常に役立ちます。

例えば、設定情報や定数のような変更されるべきではないデータを扱う際に有用です。

さらに、この手法を利用すると、関数の戻り値としても使用できます。例えば、APIから取得したデータを変更不能として返す際にも活用することができます。

その結果、関数の使用者は意図しないデータの変更を避けることができるので、バグのリスクを減少させることができます。

●注意点と対処法

TypeScriptのインデックスシグネチャを効果的に利用するためには、その持つ特性や可能性を最大限に活かすことが求められます。

しかし、その過程で直面するかもしれない様々な注意点や問題についても理解しておくことが大切です。

それでは、TypeScriptのインデックスシグネチャを使用する際の代表的な注意点と、それに対する対処法をいくつか紹介します。

○不正確な型定義によるバグの発生

このコードでは、インデックスシグネチャの型定義において、不正確な定義をすることによる問題を表しています。

この例では、オブジェクトに想定外のプロパティを追加することで問題が発生しています。

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

const obj: MyObject = {};
// 想定外のプロパティを追加
obj["unexpectedKey"] = "This is a string";

上記のコードでは、MyObject型のオブジェクトに、文字列の値を持つプロパティを追加しています。

しかし、インデックスシグネチャの定義によれば、すべてのプロパティの値は数値であるべきです。

このような矛盾は、コンパイル時にエラーとして検出されるため、事前に問題を修正することができます。

対処法としては、型定義を正確に行い、不要なキーの追加や予期しない型の代入を避けることが必要です。

また、コンパイルオプションのstricttrueに設定することで、より厳格な型チェックを行うことができます。

○特定のキーのみを許容したい場合

インデックスシグネチャは、任意のキーとその対応する値をオブジェクトに追加することを許容します。

しかし、特定のキーのみを許容したい場合には、この性質が逆に障害となることもあります。

このコードでは、特定のキーのみを許容するインデックスシグネチャの使用法を表しています。

この例では、allowedKeysという名前のキーのみを許容するインデックスシグネチャを定義しています。

type AllowedKeys = "key1" | "key2" | "key3";

interface SpecificKeysObject {
    [key in AllowedKeys]?: number;
}

const validObj: SpecificKeysObject = {
    key1: 10,
    key2: 20
};

const invalidObj: SpecificKeysObject = {
    key4: 30  // エラー:'key4' does not exist in type 'SpecificKeysObject'
};

このように、特定のキーのみを許容したい場合は、マッピング型を利用してインデックスシグネチャのキーを制限することができます。

この方法を採用することで、許容されていないキーの使用をコンパイル時に検出できるようになります。

●カスタマイズ方法

TypeScriptのインデックスシグネチャは非常に柔軟性が高いため、プロジェクトの要件に応じてさまざまなカスタマイズが可能です。

ここでは、いくつかのカスタマイズ方法を詳細なサンプルコードとともに紹介します。

○特定のプロパティを固定しつつ、インデックスシグネチャを組み合わせる

このコードでは、nameというプロパティを持つオブジェクトを定義しつつ、それ以外のプロパティはインデックスシグネチャを使用して動的に追加する方法を表しています。

この例では、nameを固定して、それ以外のプロパティを動的に追加しています。

interface CustomObject {
    name: string;
    [key: string]: string | number;
}

const obj: CustomObject = {
    name: "Taro",
    age: 25,
    country: "Japan"
};

console.log(obj.name); // Taro
console.log(obj.age);  // 25

このようにして定義すると、nameプロパティは必須となり、他の任意の文字列キーと、文字列または数値の値を持つプロパティを追加することができます。

○特定のキーセットを許可するインデックスシグネチャ

このコードでは、特定のキーセットのみを許可するインデックスシグネチャの作成方法を表しています。

この例では、"name" | "age" | "country"のいずれかのキーのみを許可しています。

type AllowedKeys = "name" | "age" | "country";

interface LimitedKeysObject {
    [key in AllowedKeys]?: string | number;
}

const obj2: LimitedKeysObject = {
    name: "Hanako",
    age: 30
};

console.log(obj2.name); // Hanako
console.log(obj2.age);  // 30

このような定義を使用すると、特定のキーセットのみを許可するオブジェクトを作成することができます。

"job"のような定義されていないキーを追加しようとすると、コンパイルエラーが発生します。

○インデックスシグネチャと関数を組み合わせる

このコードでは、インデックスシグネチャを持つオブジェクトを関数の引数として受け取り、それを元に処理を行う方法を表しています。

この例では、キーと値を繰り返し表示しています。

function printObject(obj: { [key: string]: string }) {
    for (const [key, value] of Object.entries(obj)) {
        console.log(`${key}: ${value}`);
    }
}

const obj3 = {
    firstName: "Yamada",
    lastName: "Tarou"
};

printObject(obj3);
// 出力内容
// firstName: Yamada
// lastName: Tarou

この関数は、任意のキーと文字列の値を持つオブジェクトを受け取ることができ、それを繰り返し処理して表示します。

まとめ

TypeScriptのインデックスシグネチャは、多様なデータ構造を表現するための強力な機能であることがお分かりいただけたかと思います。

この記事を通して、TypeScriptのインデックスシグネチャの真価とその活用方法についての深い理解を得ることができたかと思います。

日々のコーディングに役立てることで、より品質の高いアプリケーションやライブラリを開発する一助となることを願っています。