4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【TypeScript】Index Signature インデックスシグネチャ

Posted at
  • オブジェクトのメンバーに、文字列や数値でアクセスできるようになります。
  • 参照するプロパティを動的に変える場合などに使います。

使用例

// 元素
interface IChemistryElement {
  readonly symbol: string
  readonly atomicWeight: number
}
// 周期表(=元素の一覧)
const PeriodicTable = new class {

  // インデックスシグネチャ
  readonly [key: string]: IChemistryElement | undefined

  public readonly hydrogen: IChemistryElement = { symbol: 'H', atomicWeight: 1.008 }
  public readonly helium: IChemistryElement = { symbol: 'He', atomicWeight: 4.003 }
}

// 普通はこのようにしか参照できないところ、
console.log(PeriodicTable.hydrogen.atomicWeight) // '1.008'
// インデックスシグネチャを使うと文字列で参照できるようになります。
console.log(PeriodicTable['hydrogen'].atomicWeight) // '1.008'

例えば以下のような書き方が可能になります。

/*
  <button onclick="showSymbol('hydrogen')">水素の元素記号を表示します</button>
  <button onclick="showSymbol('helium')">ヘリウムの元素記号を表示します</button>
*/
function showSymbol (elementName: string) {
  alert(`元素記号は ${PeriodicTable[elementName].symbol} です。`)
}

メンバーの動的な追加

存在しないメンバーに代入してもエラーとはならず、自動的にプロパティが生成されます。
これを避けるには、インデックスシグネチャの定義にreadonlyを指定します。動的な追加ができなくなります。

readonlyなし
const PeriodicTable = new class {
  [key: string]: IChemistryElement | undefined
}
// OK
PeriodicTable.lithium = { symbol: 'Li', atomicWeight: 6.941 }
PeriodicTable['beryllium'] = { symbol: 'Be', atomicWeight: 9.012 }
readonlyあり
const PeriodicTable = new class {
  readonly [key: string]: IChemistryElement | undefined
}
// コンパイルエラー
PeriodicTable.lithium = { symbol: 'Li', atomicWeight: 6.941 }
PeriodicTable['beryllium'] = { symbol: 'Be', atomicWeight: 9.012 }

主な注意点

キーの型の制約

シグネチャのキーにできるのはstringnumberのみです。

メンバーの型の制約

そのクラスにはシグネチャで指定した型のメンバーしか定義することができなくなります。

const PeriodicTable = new class {
 [key: string]: IChemistryElement

  // エラー
  // 型 'boolean' のプロパティ 'aaa' を文字列インデックス型 'IChemistryElement' に割り当てることはできません。
  aaa = true
}

存在しないプロパティ

存在しないメンバーを指定することも可能です。

console.log(PeriodicTable['spacium']) // undefined

ただしシグネチャで定義した通りの型で認識されてしまうため、型の安全性を破壊する可能性があります。

// 存在しないメンバー"spacium"のプロパティを参照しようとして実行時エラーになる
console.log(PeriodicTable.spacium.atomicWeight)

コンパイラがundefinedの可能性を検知できるようにするには、シグネチャの型に | undefinedを加える必要があります。

危ない
const PeriodicTable = new class {
 [key: string]: IChemistryElement
  public readonly hydrogen: IChemistryElement = { symbol: 'H', atomicWeight: 1.008 }
}
const h = PeriodicTable.hydrogen // IChemistryElement
const s = PeriodicTable.spacium // IChemistryElement <- !?
安全
const PeriodicTable = new class {
  [key: string]: IChemistryElement | undefined // undefined を追加
  public readonly hydrogen: IChemistryElement = { symbol: 'H', atomicWeight: 1.008 }
}
const h = PeriodicTable.hydrogen // IChemistryElement
const s = PeriodicTable.spacium // IChemistryElement | undefined <- OK!

所感

  • 区分オブジェクトの表現に適していそうと感じました
  • 例えばVueでデータテーブルのコンポーネントを作ろうと思ったら、行をジェネリッククラスにしてそのプロパティを動的に参照すると綺麗になりそうです
<template>
  <table>
    <tr v-for="row in data" :key="row">
      <td v-for="key in dataKeys" :key="key">
        {{ row[key] }}
      </td>
    </tr>
  </table>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'

@Component({})
export default class GridView<T extends { [key: string]: any }> extends Vue {
  @Prop() public data!: T[]
}
</script>

参考

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?