#software-engineering/typescript
1. the factory is in an abstract class `Test` constructor
```ts
interface LifeCycle {
onReady(...args: unknown[]): void;
}
// a factory function
function createInstance<T extends LifeCycle, B extends unknown[]>(
temp: new (...args: B) => T,
...args: B
): T {
const instance = new temp(...args);
instance.onReady();
return instance;
}
abstract class A {
constructor() {}
}
class A1 extends A {}
class A2 extends A {}
class A3 extends A {}
type BCP = [a: A];
abstract class B {
public a: A;
constructor(...[a]: BCP) {
this.a = a;
}
}
class B1 extends B {}
class B2 extends B {}
class B3 extends B {}
type CCP = [b: B];
abstract class C {
public b: B;
constructor(...[b]: CCP) {
this.b = b;
}
}
class C1 extends C {}
class C2 extends C {}
class C3 extends C {}
// To get started, we need a type which we'll use to extend
// other classes from. The main responsibility is to declare
// that the type being passed in is a class.
type Constructor<T = {}> = new (...args: any[]) => T;
// now A, B and C don't know the existence of Test
// need to add a reference back to Test to A B and C
// we need to use mixin
function withTestReference<T extends Constructor, K extends Test = Test>(Base: T) {
return class extends Base {
test: K;
constructor(test: K, ...args: any[]) {
super(...args);
this.test = test;
}
};
}
type withTestReference<T, K extends Test = Test> = Prettify<
T & InstanceType<ReturnType<typeof withTestReference<Constructor<new () => T>, K>>>
>;
abstract class Test implements LifeCycle {
a: withTestReference<A>;
b: withTestReference<B>;
c: withTestReference<C>;
/**
* the constructor accept the constrcutors of A, B, C
*/
protected constructor(
constructorA: new () => A,
constructorB: new (...args: BCP) => B,
constructorC: new (...args: CCP) => C
) {
const AWithTestReference = withTestReference(constructorA);
// @ts-ignore
this.a = new AWithTestReference(this);
// do something here
this.onAReady();
const BWithTestReference = withTestReference(constructorB);
// @ts-ignore
this.b = new BWithTestReference(this, this.a);
// do something here
this.onBReady();
const CWithTestReference = withTestReference(constructorC);
// @ts-ignore
this.c = new CWithTestReference(this, this.b);
// do something here
this.onCReady();
}
abstract onReady(): void;
onAReady(): void {}
onBReady(): void {}
onCReady(): void {}
}
class Test1 extends Test {
a: withTestReference<A1, Test1>;
b: withTestReference<B1, Test1>;
c: withTestReference<C1, Test1>;
value = 1;
constructor() {
super(A1, B1, C1);
}
onReady(): void {
console.log(this.a, this.b, this.c);
}
}
class Test2 extends Test {
a: withTestReference<A2, Test2>;
b: withTestReference<B2, Test2>;
c: withTestReference<C2, Test2>;
value = 2;
constructor() {
super(A2, B2, C2);
}
onReady(): void {
console.log(this.a, this.b, this.c);
}
}
class Test3 extends Test {
a: withTestReference<A3, Test3>;
b: withTestReference<B3, Test3>;
c: withTestReference<C3, Test3>;
value = 3;
constructor() {
super(A3, B3, C3);
}
onReady(): void {
console.log(this.a, this.b, this.c);
}
}
const test1 = new Test1();
const test2 = new Test2();
const test3 = new Test3();
console.log(test1.a.test.value);
console.log(test2.a.test.value);
console.log(test3.c.b.a.test.value);
```