【TypeScript】 Object[key]() （ブラケット記法）で関数呼び出ししたら Element implicitly has an 'any' type でハマった話

##【問題】ブラケット表記法でメソッド呼び出しするとコンパイルが通らない。
```TypeScript
静的クラスに実装したメソッドをブラケット表記法で呼び出します。

const Klazz = {
  doHoge() {
    console.log('done hoge');
  },
  doFuga() {
    console.log('done fuga');
  },
  doPiyo() {
    console.log('done piyo');
  }
};

const key: string = 'doFuga';
Klazz.doFuga[key]();  // Element implicitly has an 'any' type because type '() => void' has no index signature.

```
> Element implicitly has an 'any' type because type '() => void' has no index signature.
> '（）=> void'型にはインデックスの署名がないため、要素は暗黙的に 'any'型です。

## 【原因】静的クラスの **key** が **any** 型であるため。

```TypeScript
下記のふたつは同じ意味

Klazz.doFuga();     // ドット表記法
Klazz['doFuga']();  // ブラケット表記法
```

**JavaScript** では **```Obeject[key]()```** でメソッドを呼ぶ際に **key** を変数にすると動的にメソッドを呼び出すことができます。
しかし、**TypeScript** では **key** を変数にするとコンパイラに怒られます。

```TypeScript
const key = 'doHoge';
Klazz[key]() エラー！
```

（ **```Klazz['doHoge']```** とリテラルを直接埋め込む場合はコンパイルが通ります。）

##【対策】 **interface** を定義してメソッドを定義するオブジェクトに実装する。

```TypeScript
interface IKlazz {
  [key: string]: Function;  // ←シグネチャー
  doHoge(): void;
  doFuga(): void;
  doPiyo(): void;
}

const Klazz: IKlazz = {
  doHoge() {
    console.log('done hoge');
  },
  doFuga() {
    console.log('done fuga');
  },
  doPiyo() {
    console.log('done piyo');
  }
};

const key: string = 'doFuga';
Klazz[key]();  // done fuga
```

**interface** を定義することでコンパイラに型を知らせます。
シグネチャ **```[key: string]: Function;```** には **key** が **string** 、戻り値が関数と定義します。
なぜ、シグネチャの戻り値が **void** でなく **Function** であるかはトランスパイルされた **JavaScript** を見るとわかります。

```JavaScript:トランスパイルされたJavaScript
var Klazz = {
    doHoge: function () {
        console.log('done hoge');
    },
    doFuga: function () {
        console.log('done fuga');
    },
    doPiyo: function () {
        console.log('done piyo');
    }
};
```

トランスパイルされたコードでは **key** ：文字、 **value** ：**匿名関数**の連想配列になっています。

## 【サンプル】静的クラスに登録されたメソッドを順に動的に呼び出す。

```JavaScript
interface IKlazz {
  [key: string]: Function;  // シグネチャ― 
  doHoge(): void;
  doFuga(): void;
  doPiyo(): void;
}

/* 状態を持たせたくないので不変オブジェクトとして生成しました。 */
const Klazz: IKlazz = Object.freeze({
  doHoge() {
    console.log('done hoge');
  },
  doFuga() {
    console.log('done fuga');
  },
  doPiyo() {
    console.log('done piyo');
  }
});

const myKeys: string[] = ['doFuga', 'doPiyo'];

Object.keys(Klazz).forEach(key => {
  const isMyKey = myKeys.some(myKey => myKey === key);
  if (isMyKey) {
     Klazz[key]();  // done fuga, done piyo
  }
});
```

![object.png](https://qiita-image-store.s3.amazonaws.com/0/195024/7102e1c0-c00c-d0fb-60da-e5cc0db45ae7.png)

**inteface** を実装したのでブラケット表記法で **key** を変数にしてもコンパイルが通ります。
**Object.keys()** は **key** の配列を返すため **```Object.keys(Klazz).forEach(key => Klazz[key]());```** で **Klazz** のメソッドをすべて実行できます。
上記の例ではメソッドを動的に呼び出す例のため、配列 **myKeys** に格納された **key** 名のメソッドのみ実行しました。

## 参考にしたウェブページ

- [Typescriptで、Object[key]とすると出るIndex signature of object type implicitly has an 'any' type.を正しく回避する - qiita](http://qiita.com/gonta/items/fb7b9e6d0f12060c27d6)
- [How do I prevent the error “Index signature of object type implicitly has an 'any' type” when compiling typescript with noImplicitAny flag enabled? - stackoverflow](https://stackoverflow.com/questions/32968332/how-do-i-prevent-the-error-index-signature-of-object-type-implicitly-has-an-an)
- [TypeScriptで学ぶJavaScript入門：第9回　連想配列の取り扱い方 (4/4) インデックスや要素のデータ型を指定するには - @IT](http://www.atmarkit.co.jp/ait/articles/1501/29/news117_4.html)
- [Suggestion: Add signature for Object.keys() to remove "element implicitly has an 'any'" error #13989 -GitHub](https://github.com/Microsoft/TypeScript/issues/13989)
- [ES6のconstを使い倒すレシピ2 - Object.freeze編 〜 JSおくのほそ道 #035 -qiita](http://qiita.com/hosomichi/items/84b05c1b0c09d26cd11e)
- MDN web docs
    - [Object.freeze()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)
    -  [Object.keys()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
    -  [Array.prototype.some()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/some)