I am working on converting an existing codebase to TypeScript.
While I recognize the value of TypeScript's Mixin infrastructure, I need to support a codebase that simply copies properties from a common module to a class instance.
Here is a very simplified example to illustrate the structure I'm trying to support:
// Common base class
class BaseClass {
baseFeature():boolean {
return true;
}
}
// Common mixin module
const mixin = {
mixinFeatureOne():boolean {
return this.baseFeature();
},
mixinFeatureTwo():boolean {
return this.mixinFeatureOne();
}
} as ThisType<BaseClass>;
// Specific module that extends the base class and combines the mixin
class DerivedClass extends BaseClass {
constructor() {
super();
Object.assign(this, {
...mixin,
});
}
}
const instance = new DerivedClass();
TypeScript is choking on the mixin object.
Without a lot of further modification, I seem to be limited to two options:
EITHER Leave the mixin object implicitly typed (that is, the default inferred type) in which case I can't access functions of the base type
OR I can define the mixin object as
ThisType<BaseClass>
which gives me access to the base class functions, but I can no longer access functions within the mixin.
I'm sure I'm doing something wrong here, but the only solution I've found so far is to create a type that duplicates the mixin object then declares the mixin as a combination of the ThisType and the mixin type. This is very tedious as it requires duplicating the definition every time a function is added or modified.
type MixinType = BaseClass & {
mixinFeatureOne():boolean;
mixinFeatureTwo():boolean;
};
const mixin = {
mixinFeatureOne():boolean {
return this.baseFeature();
},
mixinFeatureTwo():boolean {
return this.mixinFeatureOne();
}
} as ThisType<MixinType>;
Maybe this is just the penalty that must be paid for such a non-conforming mixin strategy, but I feel like this should be a bit easier. Is there a way we could simplify this by extending the inferred type instead of redeclaring it? Something like this:
const mixin = {
mixinFeatureOne():boolean {
return this.baseFeature();
},
mixinFeatureTwo():boolean {
return this.mixinFeatureOne();
}
} as ThisType<BaseClass & __inferred_type__>;
(I am also aware that I'm going to run into issues with the DerivedType
not having the mixin features. I'm happy to leave that for a future question (if I can't figure it out on my own), but I'm grateful if, in addition to the main question, you have any advice or recommendations in that regard.)