ややこしい関数の型ですが、一般化したいです
Q&A
解決したいこと
「ある変数がinterface Hogeを満たす前提の下、更にinterface Fuga(Fuga extends Hoge)である場合、Fugaに対応するclassインスタンスを生成する」という処理を書いていました。
生成するclassインスタンスですが、Hogeに対応するclass HogeClassは作成し、Fugaに対応するFugaClass extends HogeClassも作成しました。
FugaClass特有の処理やパラメータがあるので、FugaClass生成時にconstructorに渡す引数の型はFugaに限るようにしました。
そうすると、HogeであるからといってFugaであるとは限らないので、Fugaであるかどうかの判定が必要になります。
この判定の為に、(hoge: Hoge) => hoge is Fuga型の関数を生成し、利用するようにしました。
実際の対象はもっとややこしいですが、それらしく簡単に書くと以下のようになります。
interface Hoge {
hoge: string
}
interface Fuga extends Hoge {
fuga: string
}
function isFuga(hoge: Hoge):hoge is Fuga {
return typeof hoge.fuga == 'string';
}
function createHogeClass(hoge: Hoge): HogeClass {
if (isFuga(hoge)) {
return new FugaClass(hoge);// FugaClass extends HogeClass
} else {
return new HogeClass(hoge);
}
}
問題は、書いている処理内でFugaに相当する型が、それなりの数(20個以上?)存在している点です。
コードの構成の分かりやすさを考えると、isXX系の関数をまとめておく変数があった方が良いと考えました。これに型を付けようと考えた時、表題のややこしい関数の型が問題になりました。
// もしかしたらもっと良いデータ構造があるかも
// keyは特定値のいずれかという形式にできるとは思うものの、まだ網羅できていないので一旦stringにしています
const isInterfaceOf: {[key: string]: /* ここの型がどうなるのか知りたいです */} = {
Fuga: (hoge: Hoge): hoge is Fuga => {/* ... */},
Piyo: (hoge: Hoge): hoge is Piyo => {/* ... */},
// ...
}
実際のコードでは、再帰的にcreateClassに相当する関数を実行することになるので、できれば関連する処理は容易にcreateClassから呼び出せるようにしたいです。
発生している問題・エラー
上記isInterfaceOfに指定した型と、isInterfaceOf.Fuga等の型のすり合わせが上手くいかないという状況にあります。
該当するソースコード
大凡上記「解決したいこと」の通りです。
自分で試したこと
型ジェネリクスを用いた記法(<T extends Hoge>(hoge: Hoge): hoge is T)なども試してみましたが、慣れていないのもあり、全体が上手く動作するように記述するのには失敗しました。