##【問題】ブラケット表記法でメソッド呼び出しするとコンパイルが通らない。
静的クラスに実装したメソッドをブラケット表記法で呼び出します。
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 型であるため。
下記のふたつは同じ意味
Klazz.doFuga(); // ドット表記法
Klazz['doFuga'](); // ブラケット表記法
JavaScript では Obeject[key]()
でメソッドを呼ぶ際に key を変数にすると動的にメソッドを呼び出すことができます。
しかし、TypeScript では key を変数にするとコンパイラに怒られます。
const key = 'doHoge';
Klazz[key]() エラー!
( Klazz['doHoge']
とリテラルを直接埋め込む場合はコンパイルが通ります。)
##【対策】 interface を定義してメソッドを定義するオブジェクトに実装する。
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 を見るとわかります。
var Klazz = {
doHoge: function () {
console.log('done hoge');
},
doFuga: function () {
console.log('done fuga');
},
doPiyo: function () {
console.log('done piyo');
}
};
トランスパイルされたコードでは key :文字、 value :匿名関数の連想配列になっています。
【サンプル】静的クラスに登録されたメソッドを順に動的に呼び出す。
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
}
});
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
- How do I prevent the error “Index signature of object type implicitly has an 'any' type” when compiling typescript with noImplicitAny flag enabled? - stackoverflow
- TypeScriptで学ぶJavaScript入門:第9回 連想配列の取り扱い方 (4/4) インデックスや要素のデータ型を指定するには - @IT
- Suggestion: Add signature for Object.keys() to remove "element implicitly has an 'any'" error #13989 -GitHub
- ES6のconstを使い倒すレシピ2 - Object.freeze編 〜 JSおくのほそ道 #035 -qiita
- MDN web docs