Im trying to define a pure class based mixin function, but I cannot get the type signature right for this one.
The intent is to provide a function that accepts any Class A as parameter and returns a new Class B that extends the original Class A.
export function mixin<A>(myclass: A) {
return class B extends A {
newMethod() {
//stuff
}
}
}
As I said, I cannot figure this out.
I need a way to express that A needs to be a class.
I also need to express the return type, which yields several errors, among:
error TS4060: Return type of exported function has or is using private name 'B'.
Additional information:
utils.ts
module that is exported so that other modules can use itThere's an open issue for this one: Allow class to extend from a generic type parameter
For now you can work around that with something like this:
interface Base{}
interface BaseClass<T> {
new (): T
readonly prototype: T;
}
function mixin<T extends Base>(baseClass: BaseClass<T>) {
class B extends (baseClass as BaseClass<Base>) {
newMethod() { }
}
return B as BaseClass<T & B>;
}
(code in playground)
Which is based on code from here: extends dynamic Base class with generic throw an error
You can define an interface for the methods which will be added by the new class, something like:
interface B {
newMethod(): void;
}
function mixin<T extends Base>(baseClass: BaseClass<T>): BaseClass<T & B> {
class BImpl extends (baseClass as BaseClass<Base>) implements B {
newMethod() {
console.log("B.newMethod");
}
}
return BImpl as BaseClass<T & B>;
}
(code in playground)
Then you can export the B
interface and then you can use it anywhere.
This code works well:
class A implements Base {
method() {
console.log("A.method");
}
}
let NewA = mixin(A);
let newA = new NewA();
newA.method();
newA.newMethod();
Output:
A.method
B.newMethod
I wanted to use mixins in TypeScript 2.2 with
@Parameter
)Feature
) and mixin application (Derived
) from module
"declaration": true
in tsconfig.json)I started with following code which unfortunately does NOT work:
export type Constructor<T> = new(...args: any[]) => T;
export abstract class AbstractBase {
fieldA = "a";
}
export function Feature<T extends Constructor<object>>(Base: T) {
return class extends Base {
@Parameter()
fieldB = "b";
};
}
export class Derived extends Feature(AbstractBase) {
fieldC = "c";
}
Finally I ended up with following code which is more complicated but works as expected:
export type Constructor<T> = new(...args: any[]) => T;
export abstract class AbstractBase {
fieldA = "a";
}
export interface Feature {
fieldB: string;
}
export function Feature<T extends Constructor<object>>(Base: T): T & Constructor<Feature> {
class TWithFeature extends Base implements Feature {
@Parameter()
fieldB = "b";
}
return TWithFeature;
}
export const AbstractBaseWithFeature = Feature(AbstractBase as Constructor<AbstractBase>);
export class Derived extends AbstractBaseWithFeature {
fieldC = "c";
}
In addition to @bruno-grieder answer see following links:
abstract constructor type - https://github.com/Microsoft/TypeScript/issues/5843
decorators within mixins - https://github.com/Microsoft/TypeScript/issues/14607
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With