I've been hitting my head against this problem for some days now and seem to have reached my TS capabilities and would be very grateful for some assistance.
I have some fairly convoluted types but I will try to simplify it for this purpose.
Say I have two classes Score and Metric that each have two derived classes that should implement other methods and have slightly different properties, so I want to make them generic. The scores can all derive from the same base class but my Metrics need to derive from different classes that I don't own.
I thought I could achieve this with Mixins, i.e. define the common methods in a class factory and mix in the constructor for either of the implementations but I fail at that condition
Throughout my project I have been using interfaces A and B to differentiate between the two different functionalities or keep them as generic Kind which extends A or B where the share functionality resides.
However, now I face the issue that I need to use a different constructor depending on the implementation rather than just a type. For my purposes I don't actually need access to the additional methods of the derived classes but I need to create objects at runtime that inherit the correct method from either A or B I suspect that this might not actually have a solution and I would need to move away from my A, B conditioning pattern but to be honest I don't know how else to handle my inheritances...
If anybody could confirm my suspicions whether there is a solution for this or whether I've hit a roadblock here and if so could recommend a better pattern for my abstractions, I would be immensely grateful
I ended up using this code. Kellys gave me the right hint, but for me, having multiple of these patterns with the main differentiation being the Kind, this works a little better. I pass an object of type Kind into the constructor (which is ok for me since I can do it in the constructor of the susclasses)
interface A { isA:true}
interface B { isB:true}
class BaseA {
update(){/**do something */}
}
class BaseB {
update(){/**do something else */}
}
interface Base{
update:()=>void;//the methods here should be available when referencing a generic metric
}
type BaseConstructor = new (...args:any[])=>Base
function MetricFactory<MetricType extends BaseConstructor>(BaseX:MetricType){
return class extends BaseX{
commonMethod(){}
}
}
class MetricB extends MetricFactory(BaseB){}
class MetricA extends MetricFactory(BaseA){
someAdditionalMethod(){return this.commonMethod()}
}
function Metric<Kind extends A|B>(kind:Kind) {
return (kind as A).isA !== undefined ? new MetricA(): new MetricB()
}
class Score<Kind extends A|B>{
constructor(kind: Kind) {
this.genericMetric = Metric(kind) as Kind extends A ? MetricA: MetricB
}
genericMetric: Kind extends A ? MetricA: MetricB
}
class ScoreA extends Score<A>{
constructor(){super({isA:true})}
}
let myScore = new ScoreA()
myScore.genericMetric.update() //does something
myScore.genericMetric.commonMethod() //does common thing
myScore.genericMetric.someAdditionalMethod() //does additional thing