Typescript 如何使对象工厂维护类型

Typescript 如何使对象工厂维护类型,typescript,design-patterns,factory,Typescript,Design Patterns,Factory,我创建了以下对象工厂来实例化我所有接口的实现: interface-SomeInterface{ get():字符串; } 类实现实现了一个接口{ 构造函数(){} 得到(){ 返回“Hey:D”; } } 类型可注射={ [键:字符串]:()=>未知; }; //deno lint忽略首选常量 让默认_实现:可注入={ SomeInterface:()=>新实现(), }; 让MOCK_实现:Injectable={}; 阶级工厂{ 静态getInstance(interfaceName:st

我创建了以下对象工厂来实例化我所有接口的实现:

interface-SomeInterface{
get():字符串;
}
类实现实现了一个接口{
构造函数(){}
得到(){
返回“Hey:D”;
}
}
类型可注射={
[键:字符串]:()=>未知;
};
//deno lint忽略首选常量
让默认_实现:可注入={
SomeInterface:()=>新实现(),
};
让MOCK_实现:Injectable={};
阶级工厂{
静态getInstance(interfaceName:string,参数:unknown=[]){
if(模拟实现[interfaceName])
返回MOCK_实现[interfaceName]();
返回默认的_实现[interfaceName]();
}
静态mockWithInstance(interfaceName:string,mock:unknown){
MOCK_实现[interfaceName]=()=>MOCK;
}
}
导出常量ObjectFactory={
getInstance(名称:string):T{
将Factory.getInstance(name)返回为T;
},
mockWithInstance:Factory.mockWithInstance,
};
const impl=ObjectFactory.getInstance(“SomeInterface”);
如您所见,此工厂允许您实例化和模拟这些接口。主要的问题是,我必须使用接口和接口的名称调用此函数,以便在赋值中保留类型:

ObjectFactory.getInstance(“SomeInterface”)
我看到了,但我不喜欢使用
Base
接口。此外,这种方法也不能保持这种类型

理想情况下,我希望使用我的方法,但不必使用接口,也就是说,只使用接口的名称

旁注:
Injectable
的声明是一种使代码正常工作的技巧,理想情况下,我希望能够只使用实现名称,即:

let DEFAULT_实现={
SomeInterface:实现
}

由于您有一个关心支持的名称到类型映射的固定列表,因此这里的一般方法是根据表示此映射的对象类型
T
来考虑,然后对于任何受支持的接口名称
K extends keyof T
,您将处理以该名称返回属性的函数。。。即,类型为
()=>T[K]
的函数。另一种说法是,我们将使用它来帮助您的工厂提供型号

对于
T
,我们将只使用一个具体的类型,如
{“SomeInterface”:SomeInterface;“Date”:Date}
,但是在接下来的过程中,如果
T
是泛型的,编译器将更容易处理事情。下面是
ObjectFactory
maker的一个可能的通用实现:

function makeFactory<T>(DEFAULT_IMPLEMENTATIONS: { [K in keyof T]: () => T[K] }) {
  const MOCK_IMPLEMENTATIONS: { [K in keyof T]?: () => T[K] } = {};
  return {
    getInstance<K extends keyof T>(interfaceName: K) {
      const compositeInjectable: typeof DEFAULT_IMPLEMENTATIONS = {
        ...DEFAULT_IMPLEMENTATIONS,
        ...MOCK_IMPLEMENTATIONS
      };
      return compositeInjectable[interfaceName]();
    },
    mockWithInstance<K extends keyof T>(interfaceName: K, mock: T[K]) {
      MOCK_IMPLEMENTATIONS[interfaceName] = () => mock;
    }
  }
}
这一切都如我所想。我们使用所需的
DEFAULT\u实现调用
makeFactory
来创建
ObjectFactory
。这里我已经注释了
SomeInterface
属性返回类型为
SomeInterface
的值(否则编译器将推断
实现
,这可能比您希望的更具体)

然后我们可以看到,编译器允许我们使用正确的参数调用
ObjectFactory.getInstance()
ObjectFactory.mockWithInstance()
,并返回预期的类型,而且它在运行时也可以工作



编译时是否知道接口列表?或者随着时间的推移,你会添加新的吗?我希望这里的代码是这样的:这里唯一存在的问题是您正在询问的问题,一些行显示了预期与实际的行为。现在为了处理这个问题,我必须处理
ConcreteImplementation
是什么,我在代码中没有看到用例。我已经编辑了示例,以防您需要更多信息,我在大学项目中尝试使用干净的体系结构创建解析器,在此项目中,我简化了用例:。您可以使用断言函数“动态”将接口添加到列表中,但这比仅使用固定列表更复杂、更脆弱。这些编辑会产生问题,因为它们有语法错误(大引号无效)和其他问题(
new Implementation()
不是有效的
()=>未知的
,没有名为
getInstance()
)的函数)请考虑将示例代码放入IDE并在发布之前测试它的行为。我现在必须退出,但我的问题仍然是:您是否需要通过<代码> MoCKION StIN()添加接口?
,还是只使用现有接口的固定列表?前者可以使用断言函数,但后者更简单。很抱歉,它现在已经固定。是的,我使用现有接口的固定列表。我导入它们并像SomeInterface一样添加它们。首先,非常感谢您令人难以置信的回答。我只是想知道如果有可能摆脱SomeInterface:()=>new SomeImplementation()hack。我的意思是,将可用接口声明为SomeInterface:SomeImplementation,并在makeFactory函数中创建它们。您就是编写
SomeInterface:()=>new Implementation()的人
在示例代码中,因此我不确定这到底是一个什么样的黑客行为?您是否在寻找类似于maybe的东西?如果不是,请详细说明,但请记住,这可能超出了所问问题的范围,无法在此处进一步讨论。这是一个黑客行为,因为我必须这样做才能使我的旧解决方案工作:/。我的意思是:s只是使用{SomeInterface:SomeImplementation}作为makeFactory函数的参数。很抱歉,我应该说这是问题中的一个漏洞。我已经在问题中添加了一个编辑,但这是我的错,所以请随意忽略我的请求,您已经帮了我很多。
const ObjectFactory = makeFactory({
  SomeInterface: (): SomeInterface => new Implementation(),
  Date: () => new Date()
});

console.log(ObjectFactory.getInstance("SomeInterface").get().toUpperCase()); // HEY :D
ObjectFactory.mockWithInstance("SomeInterface", { get: () => "howdy" });
console.log(ObjectFactory.getInstance("SomeInterface").get().toUpperCase()); // HOWDY
console.log(ObjectFactory.getInstance("Date").getFullYear()); // 2020