#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); ```