I'v build a nice library for composition of types (mixin) in a dynamic way so the bookkeeping overhead of having to write stand-in members is not needed. (the lib also has some other features and customisation for composition).
It works great but I have one last bookkeeping overhead that bothers me, The lib requires to explicitly set the Type parameters since I can't get it to work when I let typescript infer the types.
Here's an example of what i'm facing:
TypeScript Version:
1.8.9
Code
A and B are "example" classes, we will use them for demonstration.
Each have one static member and one instance member, we will use them verify the output when mixin them together.
classA{staticSTATIC_A: string='A';instanceA: string='A';constructor(){}}classB{staticSTATIC_B: string='B';instanceB: string='B';}A "naive" approach using a simple function:
functionmergeTypes<T,Z>(type1: T,type2: Z): T&Z{// Do some work to mixin the types.return<any>{};}letAB=mergeTypes(A,B);// Inferred AB: typeof A & typeof Bletab=newAB();// Inferred ab: AAB.STATIC_A;// Compiler OK AB.STATIC_B;// Compiler OKab.instanceA;// Compiler OKab.instanceB;// Compiler ERR: Property 'instanceB' does not exist on type 'A'Conclusion: Intersection of static types will not propagate to the instance shape.
A more explicit approach with a control unit and state.
// Define a contract to bind an instance type param (T/Z) to its static "parent" (TType/ZType)interfaceConcreteTypeOf<T>extendsFunction{new(...args): T;}// Explicitly say what we are dealing with:classMergeTypes<T,TTypeextendsConcreteTypeOf<T>,Z,ZTypeextendsConcreteTypeOf<Z>>{constructor(privatetype1: TType,privatetype2: ZType){}// We return a constructor for T & Z and intersect static members.merge(): ConcreteTypeOf<T&Z>&TType&ZType{// Do some work to mixin the types.return<any>{};}}An implicit attempt first, let the compiler infer for us
letAB=newMergeTypes(A,B).merge();// Inferred AB: ConcreteTypeOf<{}> & typeof A & typeof Bletab=newAB();// Inferred ab: {}AB.STATIC_A;// Compiler OK AB.STATIC_B;// Compiler OKab.instanceA;// Compiler ERR: Property 'instanceA' does not exist on type '{}'ab.instanceB;// Compiler ERR: Property 'instanceB' does not exist on type '{}'It turns out that the compiler infers ConcreteTypeOf<T & Z> to be ConcreteTypeOf<{}>, WHY?
Finally, a working version, but we need to explicitly express what we want:
letAB=newMergeTypes<A,typeofA,B,typeofB>(A,B).merge();// Inferred AB: ConcreteTypeOf<A & B> & typeof A & typeof Bletab=newAB();// Inferred ab: A & BAB.STATIC_A;// Compiler OK AB.STATIC_B;// Compiler OKab.instanceA;// Compiler OK ab.instanceB;// Compiler OK
If i'll be able to remove the need to explicitly express the type I will be able to have an easy API for composition/mixin what ever, that can create type's on the fly and save a reference for them (compile time type reference) as if they were defined expressively.
I'v build a nice library for composition of types (mixin) in a dynamic way so the bookkeeping overhead of having to write stand-in members is not needed. (the lib also has some other features and customisation for composition).
It works great but I have one last bookkeeping overhead that bothers me, The lib requires to explicitly set the Type parameters since I can't get it to work when I let typescript infer the types.
Here's an example of what i'm facing:
TypeScript Version:
1.8.9
Code
AandBare "example" classes, we will use them for demonstration.Each have one
staticmember and oneinstancemember, we will use them verify the output when mixin them together.A "naive" approach using a simple function:
A more explicit approach with a control unit and state.
An implicit attempt first, let the compiler infer for us
Finally, a working version, but we need to explicitly express what we want:
If i'll be able to remove the need to explicitly express the type I will be able to have an easy API for composition/mixin what ever, that can create type's on the fly and save a reference for them (compile time type reference) as if they were defined expressively.