问题
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
asA
inMain
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:
- 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)
- 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 explicittype
definition in the selected answer simpler. - 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