ややこしい関数の型ですが、一般化したいです
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
)なども試してみましたが、慣れていないのもあり、全体が上手く動作するように記述するのには失敗しました。