I'm delving into the depths of class inheritance trying to unravel its mysteries. Although I have some techniques to handle static method typing, I'd like to delve into a common scenario I encounter and explore alternative perspectives.
Consider a Root
class with essential methods that every subclass should possess. These methods include public methods (main
and fork
) along with a static method main
, each accepting a single argument props
which is a plain object. Each subclass enriches this object by adding specific properties relevant to that subclass.
While constructors are unnecessary for these classes, I recognize the potential utility of utilizing argument inheritance such as
ConstructorParameters<T>[0]
. I've experimented with T['props']
without success, and also considered leveraging InstanceType<T>
for static methods using T extends Root
with this: T
defined. However, adapting this approach necessitates class-specific typing for props
.
If required, I am open to incorporating a class generic but would prefer exploring alternatives before resorting to it.
class Root {
props?: {root?: boolean} = {};
main(props?: Root['props']) {
this.props = {...this.props, ...props};
return this;
}
fork(props?: Root['props']) {
return new Root().main({...this.props, ...props});
}
static main(props?: Root['props']) {
return new this().main(props);
}
}
class Alpha extends Root {
props?: Root['props'] & {alpha?: boolean};
}
class Beta extends Alpha {
props?: Alpha['props'] & {beta?: boolean};
}
class Gamma extends Beta {
props?: Beta['props'] & {gamma?: boolean};
}
const g = Gamma.main({beta: true}); // fails beta & returns instance of Root :(
console.log(g);
How can I properly type these methods so that g
is identified as an instance of Gamma
, with the props
argument for Gamma.main
encompassing gamma
, beta
, and alpha
?