It is only the ts annotation problem, in js runtime works everything as expected.
For a multiple inheritance/mixin, we have a runtime method which takes classes/objects and creates compound(mixed) class.
class A {
a: string
static staticA: string
}
class B {
b: string
static staticB: string
}
class C extends mixin(A, B) {
c: string
static staticC: string
}
So our mixin method creates the mixed class which the C inherits. Now we have some annotation problems. Simple mixin declaration looks like this (actually, mixin accepts also objects for T1 and T2, but to make it simple I removed it from code):
interface Constructor<T = {}> {
new (...args: any[]): T;
}
declare function mixin<T1 extends Constructor, T2 extends Constructor> (
mix1: T1,
mix2: T2
): new (...args) => (InstanceType<T1> & InstanceType<T2>)
Unfortunately, the type returned by mixin looses static methods for T1 and T2.
C. /* only 'staticC' is present in autocomplete */
let c = new C;
c. /* all - 'a', 'b' and 'c' are present in autocomplete */
I also tried to return the type T1 & T2, but get the error in mixin(A, B)
[ts] Base constructors must all have the same return type.
Is there any solution for this?
Final Solution
Thanks to @titian-cernicova-dragomir. I'm adding here my final solution, I have extended the annotation to support also objects, not only classes, hopefully it will be helpful for somebody.
// Extract static methods from a function (constructor)
type Statics<T> = {
[P in keyof T]: T[P];
}
declare function mixin<
T1 extends Constructor | object,
T2 extends Constructor | object,
T3 extends Constructor | object = {},
T4 extends Constructor | object = {},
> (
mix1: T1,
mix2: T2,
mix3?: T3,
mix4?: T4,
):
(T1 extends Constructor ? Statics<T1> : {}) &
(T2 extends Constructor ? Statics<T2> : {}) &
(T3 extends Constructor ? Statics<T3> : {}) &
(T4 extends Constructor ? Statics<T4> : {}) &
(new (...args: T1 extends Constructor ? ConstructorParameters<T1> : never[]) =>
(T1 extends Constructor ? InstanceType<T1> : T1) &
(T2 extends Constructor ? InstanceType<T2> : T2) &
(T3 extends Constructor ? InstanceType<T3> : T3) &
(T4 extends Constructor ? InstanceType<T4> : T4)
);
class A {
a: string
static staticA: string
}
class B {
b: string
static staticB: string
}
const Utils = {
log () {}
}
class C extends mixin(A, B, Utils) {
c: string
static staticC: string
}
C. // has 'staticA', 'staticB', 'staticC'
let c = new C;
c. // has 'a', 'b', 'c', 'log'
I have also added arguments support from the first class's constructor (if any).
...args: T1 extends Constructor ? ConstructorParameters<T1> : never[].
Unfortunately, I couldn't find the solution to make the mixin annotation to support any number of arguments, currently I made 4, as it is enough for my case. Though our js mixin can accept any number of classes/objects to create mixed class.