Typescript 是否可以创建类型,其中函数返回值由函数参数的字符串文本选择
我一直在试图弄清楚如何为基于插件的系统实现打字,在加载一些插件之后,基本代码实际上并不知道系统支持的所有打字 我试图通过允许插件声明环境类型并以这种方式扩展原始接口来实现这一点 我希望通过一些字符串匹配的魔法,我可以让它以类似的方式工作:Typescript 是否可以创建类型,其中函数返回值由函数参数的字符串文本选择,typescript,plugins,Typescript,Plugins,我一直在试图弄清楚如何为基于插件的系统实现打字,在加载一些插件之后,基本代码实际上并不知道系统支持的所有打字 我试图通过允许插件声明环境类型并以这种方式扩展原始接口来实现这一点 我希望通过一些字符串匹配的魔法,我可以让它以类似的方式工作: type t1 = { type: 'foo'; test: number; }; type t2 = { type: 'bar'; test: string; }; type t = t1 | t2; const tx1: t = {
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');
interface PluginClass1{}
接口PluginClass2{}
接口插件API{
getPlugin:(pluginName:'plug1')=>PluginClass1
}
接口插件API{
getPlugin:(pluginName:'plug2')=>PluginClass2
}
类PluginManager实现PluginApi{
插件={};
getPlugin(pluginName){
返回这个.plugins[pluginName];
}
}
const manager=new PluginManager();
//在这里,我希望得到正确键入的返回值
//根据名称字符串(但这不起作用)
constplugin1=manager.getPlugin('plug1');
constplugin2=manager.getPlugin('plug2');
有没有什么方法可以让这种类型的输入模式正常工作,或者有没有更好的方法来输入可扩展接口?超级简单的解决方案:手动声明manager是一个插件api:
const-manager:PluginApi=new-PluginManager()
然后将plugin1
和plugin2
分别正确推断为PluginClass1
和PluginClass2
编辑:在上述解决方案中,您可能还希望将每个getPlugin
签名声明为方法,而不是指定给属性的函数。例如:getPlugin(pluginName:'plug1'):PluginClass1
而不是getPlugin:'pluginName:'plug1')=>PluginClass1
。这样就可以利用函数重载
但是,如果您不想将PluginManager
的每次使用都声明为PluginApi
,那么您必须确保实际的getPlugin
实现具有正确的类型(因为typescript不会为您这样做,尽管我不确定为什么不这样做)。因此,您需要手动为其提供正确的类型签名。一种方法是使用映射类型。但是要做到这一点,每个插件都需要声明其名称和返回类型(而不是声明方法签名本身)
interface PluginClass1{}
接口PluginClass2{}
//请注意,每个插件现在都扩展了不同的接口。宣布
//名称和返回类型,而不是方法签名。
接口插件类型{
“plug1”:PluginClass1
}
接口插件类型{
“plug2”:PluginClass2
}
//然后,我们将所有这些名称和返回类型组合到一个方法中:
接口插件API{
getPlugin(pluginName:K):PluginApiTypes[K];
}
类PluginManager实现PluginApi{
插件={};
//不幸的是,我们必须在这里复制getPlugin的签名,因为typescript不会推断它。
getPlugin(pluginName:K):PluginApiTypes[K]{
返回这个.plugins[pluginName];
}
}
const manager=new PluginManager();
//现在应该正确推断。
constplugin1=manager.getPlugin('plug1');
constplugin2=manager.getPlugin('plug2');
这也许不是一个理想的解决方案,但确实有效。也许有比我更博学的人可以解释为什么不能从界面推断出
getPlugin
的类型签名。谢谢!这个解决方案看起来像是在我的实际案例中也可以使用的东西。我自己永远也不会明白这一点:)我也从未见过keyof
声明。
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');