TL;DR

  • インデックスシグネチャはどんなキーでも受け入れるので、型安全性を損ない、自動補完も効かない。
  • 代わりに interface、Map、Record などを使う。インデックスシグネチャは、余剰プロパティチェックを無効にしたいときなどに限って使う。

内容

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

インデックスシグネチャは、type Rocket = { [property: string]: string } の [property: string]: string の部分である。次の 3 つから構成される。

  1. キーの名前: 何でもよい(型チェックでは使われない)
  2. キーの型: string、number、symbol、テンプレートリテラル型、またはこれらのユニオン型(number は避ける:項目17)
  3. 値の型: どんな型でもよい

インデックスシグネチャを避けるべき理由

インデックスシグネチャは型安全性を損なう。どんなキーでも受け入れ、特定のキーの存在を要求しないので、{} も有効な値になる。また、キーごとに異なる型を持てない。さらに、言語サービスの自動補完の恩恵も受けられない。

代替手段

型が持つフィールドがすべて分かっているなら、interface を使う。フィールドが動的に決まるなら、Map<string, string> のような Map を使う。一般に、Map のような抽象的な型は、使うときに具体的な型に変換する必要がある。このパターンは TypeScript ではよくある(項目74)。

function parseCSVMap(input: string): Map<string, string>[] { ... }
function parseRocket(map: Map<string, string>): Rocket { ... }
 
parseCSVMap(csvData).map(parseRocket);

型が限られたフィールドしか持たないなら、組み込みのマップ型である Record を使う。

type Vec3D = Record<"x" | "y" | "z", number>

インデックスシグネチャの使い道

インデックスシグネチャは、余剰プロパティチェックを無効にしたいときに使える。

declare function renderAButton(props: ButtonProps): void
interface ButtonProps {
  title: string
  onClick: () => void
  [otherProps: string]: unknown
}
 
renderAButton({
  title: "Roll the dice",
  onClick: () => alert(1 + Math.floor(6 * Math.random())),
  theme: "Solarized",
}) // インデックスシグネチャがなければ型エラーになる

また、特定のパターンに一致するプロパティだけを追加できるように制限することもできる。たとえば、data- で始まる任意のプロパティを許可する型を作れる(項目54)。

キーワード