Reputation: 133
I cannot figure out how to get the type of a typescript mixin class without resorting so some hack (exemplified below)
type Constructor<T = {}> = new(...args: any[]) => T;
function MyMixin<T extends Constructor>(BaseClass: T) {
return class extends BaseClass {doY() {}}
}
// Option A: Ugly/wasteful
const MixedA = MyMixin(class {doX() {}});
const dummy = new MixedA();
type MixedA = typeof dummy;
class OtherA {
field: MixedA = new MixedA();
a() {this.field.doX(); this.field.doY();}
}
// Option B: Verbose
class Cls {doX() {}}
interface MixinInterface {doY(): void}
const MixedB = MyMixin(Cls);
type MixedB = Cls & MixinInterface;
class OtherB {
field: MixedB = new MixedB();
a() {this.field.doX(); this.field.doY();}
}
It's really sad to me that typescript doesn't support honest mixins/traits, but is there some other way to declare the type of field
without resorting to typeof an instance or having to re-declare the signatures in an interface (I tried typeof(new MixedBaseA())
but typeof doesn't accept arbitrary expressions)?
Upvotes: 2
Views: 747
Reputation: 7257
Probably not what you want, but here's a less wasteful alternative. Given the definition:
type Constructor<T = {}> = new(...args: any[]) => T;
function MyMixin<T extends Constructor>(BaseClass: T) {
return class extends BaseClass {
doY() { }
}
}
const MixedA = MyMixin(class { doX() {} });
You can get the type using:
function getReturnType<R>(fn: (new(...args: any[]) => R)): R {
return {} as R;
}
const dummy = getReturnType(MixedA);
type MixedAType = typeof dummy;
const mixedA : MixedAType = new MixedA();
mixedA.doX();
mixedA.doY();
Proposal for getting the type of any expression is still open for discussion: https://github.com/Microsoft/TypeScript/issues/6606. This will get rid of the dummy variable and the function.
Or, to get a clean type MixedAType = MyMixinY & X
, you can choose to return the correct constructor type in the mixin:
type Constructor<T = {}> = new(...args: any[]) => T;
interface MyMixinY {
doY()
}
function MixinY<T extends Constructor>(BaseClass: T)
: Constructor<MyMixinY> & T {
return <any> class Y extends BaseClass implements MyMixinY {
doY() {
console.log("in Y");
}
}
}
const MixedA = MixinY(class X {
doX() {
console.log("in X");
}
});
function getReturnType<R>(fn: (new(...args: any[]) => R)): R {
return {} as R;
}
const dummy = getReturnType(MixedA);
type MixedAType = typeof dummy; // now is `type MixedAType = MyMixinY & X`
const mixedA: MixedAType = new MixedA();
mixedA.doX();
mixedA.doY();
Upvotes: 4