【问题标题】:Factory returning classes in TypeScriptTypeScript 中的工厂返回类
【发布时间】:2017-03-06 19:36:04
【问题描述】:

根据输入数据编写了一个库;创建类并在主类中使用它们。

为了使代码更易于维护和阅读,我已将类生成逻辑移至导出工厂函数的单独文件中。

代码是用 ES2015 编写的。现在我正在迁移到 TypeScript。

这是一个伪例子:

factory.ts

export default function (foo:string) => {
    class A {
        value:string = foo + '-A';
    }

    return { A };
};

Main.ts

import factory from './factory';

export default class Main {
    private inner:any;
    constructor(foo:string) {
        this.inner = factory(foo);
    }
    get a() {
        return new this.inner.A();
    }
}

用法:

let main = new Main('bar');
console.log(main.a.value); // "bar-A"

问题:

  • TS 编译器错误:
    Default export of the module has or is using private name 'A'.
  • 无法在Main 类中将getter a 的类型定义为A(例如get a():A { ... }

您将如何解决这个问题(将工厂类保存在单独的文件中)?还是我应该改变设计模式?

【问题讨论】:

    标签: typescript design-patterns factory


    【解决方案1】:

    怎么样:

    export interface Base {}
    
    export interface IA extends Base {
        value: string;
    }
    
    export type Builders = {
        [name: string]: { new <T extends Base>(): T };
    }
    
    export function factory(foo: string): Builders {
        class A implements IA {
            value:string = foo + '-A';
        }
    
        return { A };
    };
    

    还有:

    import { factory, IA, Builders } from './Factory';
    
    export default class Main {
        private inner: Builders;
    
        constructor(foo:string) {
            this.inner = factory(foo);
        }
    
        get a():IA {
            return new this.inner.A() as IA;
        }
    }
    

    编辑

    factory.ts 有什么问题:

    export class Base {}
    
    export type Builders = {
        [name: string]: { new <T extends Base>(): T };
    }
    
    class A extends Base {
        value: string;
    
        constructor();
        constructor(foo: string);
        constructor(foo?: string) {
            super();
            this.value = foo + "-A";
        }
    }
    
    // more classes...
    
    export function factory(foo: string): Builders {
        return { A: A.bind(A, foo) };
    };
    

    基本上和你做的一样,只是类没有在类内部定义,是导出的,所以不需要我建议的接口。
    此外,这样所有类只会被评估一次,而不是每次调用 factory 函数时。

    【讨论】:

    • 感谢这项工作。不过,我需要另外定义 4 个类的接口。
    • 如果类在函数内部声明,则不能导出类​​(作为值或类型),但编译器需要为它指定类型(使用泛型类型除外),因此您将需要为每个类公开一个接口。问题是为什么不在模块级别声明这些类?将它们包含在函数中会给您带来什么好处?
    • 我明白了。工厂函数的唯一原因是将相当复杂的内部类移动到另一个文件中,以便代码更干净。
    • 那很好,但是为什么不将类放在与工厂函数(模块级别)相同的级别并导出它们呢?那么你就不需要接口了。
    • 如示例中,foo 不会通过构造函数参数传递给内部/生成的类。所以他们需要在工厂函数中。然后我需要导出主类,以便需要在工厂之外。
    【解决方案2】:

    您的代码(OP)在 2019 年 7 月使用 Typescript 3.5.2 运行良好。

    PS。当我试图在 Typescript 中创建一个 Class Factory 时,我来到了这里。

    注意事项:

    1. Typescript 中的类定义概念有两个方面。一种是实例类型,另一种是构造函数。 (source in TS Docs)
    2. 从 Typescript 2.8 released in March 2018 开始,您可以使用 InstanceType构造函数 显式创建 实例类型。例如type classType = InstanceType&lt;typeof C&gt;。这应该使所选答案中的明确type 定义更简单。
    3. 最后,我使用以下简单技巧让我的代码正常工作:
    export default class MyNewClass
       extends ClassFactory("custom param1") {}
    

    这样我实际上将生成的类本身定义为一个新类,因此构造函数和实例类型都将被隐式创建并且是有效的。而且我不需要单独明确定义这两者。

    【讨论】:

    • InstanceType&lt;typeof C&gt; 是一个很好的发现,但是当 C 是一个抽象类时它就不起作用了。但是,在这种情况下,您可以使用类型 type classType = abstract new (...args: any) =&gt; C;
    猜你喜欢
    • 2021-07-28
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多