Factory returning classes in TypeScript

拜拜、爱过 提交于 2021-02-18 23:47:57

问题


Wrote a library that, depending on the input data; creates classes and uses them inside a main class.

To make the code more maintainable and readable, I've moved the class generation logic into a separate file that exports a factory function.

Code was written in ES2015. Now I'm migrating to TypeScript.

Here is a pseudo example:

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();
    }
}

Usage:

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

Problems:

  • TS compiler error:
    Default export of the module has or is using private name 'A'.
  • Cannot define the type of the getter a as A in Main class (e.g. get a():A { ... }

How would you resolve this (keeping the factory classes in a separate file)? Or should I change the design pattern?


回答1:


How about something like:

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 };
};

And:

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;
    }
}

Edit

What's wrong with this as 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) };
};

It's basically the same as what you did, just that the classes are not defined inside the class, and are exported so no need for the interfaces I suggested.
Also, this way all the classes will only be evaluated once and not every time the factory function is invoked.




回答2:


Your code (the OP's) is working just fine in July 2019 with Typescript 3.5.2.

PS. I got here when I was trying to create a Class Factory in Typescript.

Things to keep in mind:

  1. The concept of Class definition in Typescript has two folds. One is the instance type, and the other one is the constructor function. (source in TS Docs)
  2. Since Typescript 2.8 released in March 2018, you can explicitly create the instance type from the constructor function using InstanceType. e.g. type classType = InstanceType<typeof C>. This should make the explicit type definition in the selected answer simpler.
  3. Finally, I got my code working using a simple trick as following:
export default class MyNewClass
   extends ClassFactory("custom param1") {}

This way I am actually defining the generated class as a new class by itself, so both the constructor function and the instance-type type are going to be implicitly created and will be valid. And I don't need to explicitly define the two separately.



来源:https://stackoverflow.com/questions/42634116/factory-returning-classes-in-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!