TypeScriptの型記法「オブジェクトリテラル 」と「インデックスシグネチャ」を理解する

TypeScriptの型記法、**「オブジェクトリテラル」**と**「インデックスシグネチャ」**について、まとめます。

「オブジェクトリテラル」も「インデックスシグネチャ」も、あまり聞き慣れない言葉で、**雰囲気で理解**した気になっていませんか?

**私は、なっていました。**

私の場合、この記法に遭遇するたびに**「これって、なんだっけ？」**ってなることが多いです。

自分の理解のために、ここにまとめておきたいと思います。

誰かの理解の助けになれば幸いです。

## 初見殺しな「オブジェクトリテラル」と「インデックスシグネチャ」

例えば、以下のコードにおいて

```ts

let obj: {
    [key: string]: string // インデックスシグネチャ
} // オブジェクトリテラル
```

変数objの型が、何を意味しているか理解できますでしょうか。
私には、**初見殺し**すぎて、さっぱりわからず「？？？」となりました。

この型の意味は、「変数objは**複数のkeyを含む可能性**があり、その**すべてのstring型のkeyは、string型の値をもつ**」となります。

以下のコードにおいて、Errorの箇所では、idにnumber型の値を入れているのでエラーとなります。

```ts

let obj: {
    [key: string]: string // インデックスシグネチャ
} // オブジェクトリテラル

// OK
obj = {
    id: "123"
    token: "hogetoken"
    name: "fuganame"
}

// Error
// Type 'number' is not assignable to type 'string'.
obj = {
    id: 123
    token: "hogetoken"
    name: "fuganame"
} 
```

## オブジェクトリテラルとは

```ts

let obj: {
    id: string
    token: string
    name: string
} // オブジェクトリテラル
```

このように中括弧({})を用いて、オブジェクトの型を定義する記法のことを指します。
オブジェクトは、ハッシュテーブルの一種で、キーと値の組みを持ちます。
TypeScriptでは、多くの場合、**オブジェクトの型は「クラス」を用いて定義されます。**

しかし、用途によってはクラスを定義せずに、**即席の型定義を記述する方が便利な場合**があります。
その際に用いられるのが**「オブジェクトリテラル」**です。
「オブジェクトリテラル」は、匿名のオブジェクト型を宣言したい場合に用いられます。
(例えばJavaScriptからTypeScriptへの移行期に、**"とりあえず"**の型を定義したい場合など)

## インデックスシグネチャとは

```ts

let obj: {
    // [key: T]: U
    [key: string]: string // インデックスシグネチャ
}
```

インデックスシグネチャは、角括弧([])を用いて**「[key: T]: U」**という構文で記述されます。
これは「オブジェクトが、**複数のkey(型T)を含む可能性がある**こと」を示します。
そして、「**すべてのkey(型T)は型Uの値を持つ**こと」も示します。
オブジェクトのキーと値の型を抽象化して記述することができます。


### 備考

例えば、idだけnumber型で、その他のパラメータはstringの場合、以下のように書けるのかなと思ったのですが、これは**エラー**になります。

```ts

let obj: {
    id: number // エラー
    [key: string]: string
}

// Property 'id' of type 'number' is not assignable to string index type 'string'.
// Type '{ id: number; token: string; name: string; }' is not assignable to type 
// '{ [key: string]: string; id: number; }'. Property 'id' is incompatible with 
// index signature. Type 'number' is not assignable to type 'string'.

obj = {
    id: 123
    token: "hogetoken"
    name: "fuganame"
} 
```

keyがstringの場合は、stringの値を持つと宣言してしまっているので、

```ts

    [key: string]: string
```

**idをnumber型だと定義すると矛盾が発生してしまう**ためです。

```ts

    id: number
```


[追記] 以下のように型交差記法を用いてもエラーが発生します。
(親切な方、教えていただいてありがとうございます)

```ts
// エラー
const obj: { [key: string]: string } & { id: number } = {

  id: 123,

  token: "hogetoken",

  name: "fuganame"

};

```

よって、この場合はおとなしく、インデックスシグネチャを用いないで書きましょう。

```ts

let obj: {
    id: number
    token: string
    name: string
} // オブジェクトリテラル
```

## まとめ

・オブジェクトリテラル({})は、**オブジェクトの型を即席で定義するため**に用いられる。
・インデックスシグネチャ([key: T]: U)は、**オブジェクトが、複数のkey(型T)を含む可能性があり値はU型になる**ことを示している。

結論としては、オブジェクトリテラルもインデックスシグネチャも**クセが強い**ので、**あまり用いたくない**ですが、そうは言っても現場ではすでにオブジェクトリテラルやインデックスシグネチャを使用している箇所については、**コードを読むために理解しておきたい**と思います。

### [追記]「オブジェクトリテラル」と「オブジェクト型リテラル」の違いについて

コメント欄にて、下記のご指摘を頂きました。
ありがとうございます。


> 細かい点ですが、「オブジェクトリテラル」はオブジェクト値を構築する記法のことで、 JavaScript/TypeScript に共通しています。この記事で扱っている、オブジェクトの型を記述する TypeScript 特有の記法は「オブジェクト型リテラル」です

```ts
const obj: { n: number } = { n: 42 };
//         ^^^^^^^^^^^^^   ^^^^^^^^^---- オブジェクトリテラル
//               `---------------------- オブジェクト型リテラル
```

「オブジェクトリテラル」と「オブジェクト型リテラル」の違いについて、理解しました。
この違いはちゃんと理解しておく必要があると感じましたので引用させていただきます。