Reputation: 33644
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:
export default function (foo:string) => {
class A {
value:string = foo + '-A';
}
return { A };
};
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"
Default export of the module has or is using private name 'A'.
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?
Upvotes: 9
Views: 27497
Reputation: 29957
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:
type classType = InstanceType<typeof C>
. This should make the explicit type
definition in the selected answer simpler.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.
Upvotes: 7
Reputation: 164129
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;
}
}
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.
Upvotes: 8