2017-11-13 4 views
1

いくつかのプラグインがにロードされた後にシステムがサポートタイピング。は、関数の戻り値は、関数の引数の文字列リテラルで選択されたタイピングを、作成することが可能ことです私は、基本コードは実際にはすべて把握していないプラグインベースのシステムのためのタイピングを実装する方法を把握しようとしてきた

私は、プラグインが周囲タイピングを宣言し、そのように元のインターフェイスを拡張することを可能にすることによってこれを実現しようとしています。しかしで、だから私は、文字列に一致する可能性の同じ種類を実装したいプラグインシステムのための

type t1 = { 
    type: 'foo'; 
    test: number; 
}; 

type t2 = { 
    type: 'bar'; 
    test: string; 
}; 

type t = t1 | t2; 

const tx1: t = { 
    type: 'foo', 
    test: 1 
}; 

const tx2: t = { 
    type: 'foo', 
    test: 'this fails, because if type = foo, then test must be a number' 
}; 

const txbar: t = { 
    type: 'bar', 
    test: 'this works because with bar object test can be string' 
}; 

は、私はいくつかの文字列に一致する魔法で私はそれがこれで少し似たように動作させることができることを望んで

interface PluginClass1 {} 
interface PluginClass2 {} 

interface PluginApi { 
    getPlugin: (pluginName: 'plug1') => PluginClass1 
} 

interface PluginApi { 
    getPlugin: (pluginName: 'plug2') => PluginClass2 
} 

class PluginManager implements PluginApi { 
    plugins = {}; 
    getPlugin(pluginName) { 
    return <any> this.plugins[pluginName]; 
    } 
} 

const manager = new PluginManager(); 

// here I would like to get correctly typed return value 
// according to name string (this doesn't work though) 
const plugin1 = manager.getPlugin('plug1'); 
const plugin2 = manager.getPlugin('plug2'); 

このようなタイプのスキームを動作させる方法はありますか、あるいは拡張可能なインターフェイスを入力するための良い方法がありますか?

答えて

1

超簡単な解決策:手動でそのマネージャを宣言するには、プラグインAPIである:次に

const manager: PluginApi = new PluginManager()

plugin1plugin2が正しく、それぞれPluginClass1PluginClass2と推定されます。

上記のソリューションでは、プロパティに割り当てられた関数ではなく、getPluginシグネチャをメソッドとして宣言することもできます。例:getPlugin: (pluginName: 'plug1') => PluginClass1の代わりにgetPlugin(pluginName: 'plug1'): PluginClass1そうすれば、関数のオーバーロードを利用することができます。あなたはPluginApiとしてPluginManagerのすべての使用を宣言したくない場合は

しかし、あなたはtypescriptですが、あなたのためにこれを行うことはありませんので、(実際getPlugin実装が正しい種類を持っていることを確認する必要があります、私はなぜそうではないかは分かりませんが)。したがって、正しいタイプの署名を手動で与える必要があります。これを行う1つの方法は、マップされた型を使用することです。しかし、これを行うと、各プラグインは(メソッドのシグネチャ自体を宣言する代わりに)その名前と戻り値の型を宣言する必要があります。

interface PluginClass1 {} 
interface PluginClass2 {} 

// Note that each plugin now extends a different interface. One that declares 
// the name and return type, rather than the method signature. 
interface PluginApiTypes { 
    'plug1': PluginClass1 
} 

interface PluginApiTypes { 
    'plug2': PluginClass2 
} 

// Then we combine all those names and return types into a method: 
interface PluginApi { 
    getPlugin<K extends keyof PluginApiTypes>(pluginName: K): PluginApiTypes[K]; 
} 

class PluginManager implements PluginApi { 
    plugins = {}; 
    // Unfortunatly we have to replicate the signature of getPlugin here, since typescript will not infer it. 
    getPlugin<K extends keyof PluginApiTypes>(pluginName: K): PluginApiTypes[K] { 
    return <any> this.plugins[pluginName]; 
    } 
} 

const manager = new PluginManager(); 

// Now should be correctly inferred. 
const plugin1 = manager.getPlugin('plug1'); 
const plugin2 = manager.getPlugin('plug2'); 

これはおそらく理想的な解決策ではありませんが、機能します。たぶん、私よりも知的な人が、getPluginのタイプシグネチャがインターフェイスから推測されない理由について、いくつかの点を明らかにすることができます。

+0

ありがとうございます!このソリューションは、実際のケースでも私が働くことができるもののように見えます。私は決して自分自身を考え出したことはありません:)私は決して 'keyof'の宣言を見たことがありませんでした。 –

関連する問題