简体   繁体   English

打字稿-工厂模式

[英]Typescript - factory pattern

I'm trying to create factory for MainType . 我正在尝试为MainType创建工厂。 I would also like to reuse already created types (actually I need same instance) so I store them within ItemFactory . 我还想重用已经创建的类型(实际上我需要相同的实例),所以我将它们存储在ItemFactory

class BaseType {

}

class MainType extends BaseType {

}

class ItemFactory {
    items: { [type: string]: BaseType } = {};

    get<T extends BaseType>(type: string): T | null {
        let item = this.items[type];

        if (!item) {
            switch (type) {
                case "main-type":
                    item = new MainType();
                    break;
                default:
                    return null;
            }

            this.items[type] = item;
        }

        return item as T;
    }
}

Is there a way to simplify call 有没有一种方法可以简化通话

itemFactory.get<MainType>("main-type"); // current call

// option 1
const resolvedType = itemFactory.get<MainType>();

// option 2
const resolvedType = itemFactory.get("main-type");

I would like to have either option 1 or option 2 (no need for both), so I don't have to pass both identifier and type to have correctly resolved resulting type. 我想拥有选项1或选项2(都不需要),所以我不必同时传递标识符和类型即可正确解析结果类型。

You'll need to give the compiler some kind of mapping between names passed to itemFactory.get() and the expected output type. 您需要为编译器提供某种传递给itemFactory.get()名称与预期输出类型之间的映射。 Mapping from names to types is what interface s do best, so you can define one like this: 从名称到类型的映射是interface的最佳选择,因此您可以这样定义:

interface NameMap {
  "main-type": MainType;
  // other name-type mappings here
}

And then you change your get() method to this: 然后将您的get()方法更改为:

  get<K extends keyof NameMap>(type: K): NameMap[K] | null {
    let item = this.items[type];

    if (!item) {
      switch (type) {
        case "main-type":
          item = new MainType();
          break;
        default:
          return null;
      }

      this.items[type] = item;
    }

    return item as NameMap[K];
  }

You replace T extends BaseType to NameMap[K] where K extends keyof NameMap . 您将T extends BaseType替换为NameMap[K] ,其中K extends keyof NameMap Now the following ("option 2") will work: 现在,以下内容(“选项2”)将起作用:

const resolvedType = itemFactory.get("main-type"); // MainType | null

Note that you will never get "option 1" to work. 请注意,您将永远无法使用“选项1”。 TypeScript's type system gets erased when the JS is emitted, so this: 发出JS时,TypeScript的类型系统被删除 ,因此:

itemFactory.get<MainType>();

will become this at runtime: 在运行时将变为:

itemFactory.get();

And there's no way for that to know what to return, since the relevant information has been left behind before the code started running. 而且也没有办法知道什么回报,因为相关资料已经落伍之前的代码开始运行。 This is intentional; 这是故意的; it is not a goal of TypeScript to "add or rely on run-time type information in programs, or emit different code based on the results of the type system." TypeScript的目标不是 “添加或依赖程序中的运行时类型信息,或根据类型系统的结果发出不同的代码”。 Instead, TypeScript should "encourage programming patterns that do not require run-time metadata"... in this case it means using a runtime value like the string "main-type" instead of a design-time type like MainType to keep track of what get() should do. 相反,TypeScript应该“鼓励不需要运行时元数据的编程模式” ...在这种情况下,这意味着使用运行时值(例如字符串"main-type"而不是设计时类型(例如MainType来跟踪get()应该做什么。


Okay, hope that helps. 好的,希望对您有所帮助。 Good luck! 祝好运!

Link to code 链接到代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM