I'm currently hitting a wall in typescript.
Basically, I want to call a static function from a class extending a specific abstract class.
I get the following error
The 'this' context of type 'typeof A' is not assignable to method's 'this' of type 'AStatic<AStatic<unknown>>'. Cannot assign an abstract constructor type to a non-abstract constructor type.
Here is a link to a Typescript playground
Here is the code:
type AStatic<S extends A> = { new(): S };
abstract class A {
static callStatic<S extends AStatic<S>>(this: AStatic<S>) {
console.log('hey')
}
}
class B extends A {
}
class D extends A {
}
class C {
aType: typeof A;
constructor(type: typeof A) {
this.aType = type;
this.aType.callStatic(); // error is here
}
}
const c = new C(B);
const c_2 = new C(D);
The only way I managed to make it build in typescript is by passing any
like the instead of typeof A
. It's just a shame because I get no support from my IDE for the functions of A.
Note that I don't have control over class A and type AStatic, since those are from an external libray.
You are close! Take a look your pseudo-definition for A
:
abstract class A {
static callStatic<S extends AStatic<S>>(this: AStatic<S>) {
// ^^^^^^^^^^^^^^^^
// the `this` parameter must be type AStatic<A>
And looking at AStatic
:
type AStatic<S extends A> = { new(): A };
// ^^^^^ - type must have a constructor,
// which rules out abstract classes.
This rules out typeof A
as the this
parameter, since it is abstract
. We could try using AStatic
directly:
class C {
aType: AStatic<A>;
constructor(type: AStatic<A>) {
this.aType = type;
this.aType.callStatic();
// ^^^^^^^^^^ - Property 'callStatic' does not exist on type 'AStatic<A>'
}
}
But callStatic
is not defined on AStatic
. The solution is an intersection type:
class C {
aType: AStatic<A> & typeof A
constructor(type: AStatic<A> & typeof A) {
this.aType = type;
this.aType.callStatic() // works!
}
}
As MunsMan pointed out though, unless you override callStatic
on your derived types you don't need to pass typeof A
at all:
const c = new C(B);
c.callStatic(); // hey
B.callStatic(); // hey
D.callStatic(); // hey
In other words, as long as you have a non-abstract version of A
, you can call callStatic
(or any static method/property) on that type and it will work the same way every time!