I think this is an issue of implementing generics correctly, but I am not sure.
I created a Github gist representing the problem here: https://gist.github.com/ORESoftware/66b72b4b85262d957cb03ad097e4743e
Say I have this superclass:
class A {
foo(): A {
return this;
}
}
and several subclasses, one for example looks like so:
class B extends A {
bar(): B {
return this;
}
}
so if I do
new B().foo().bar()
this will work at runtime, but it doesn't compile with TypeScript. That's because foo() declares the return type to be A, not type B.
How can I return the type that this is, instead of declaring foo() to always return type A?
I tried this:

but I get this error:

You have to return the type of this using a polymorphic this type.
abstract class A {
foo(): this {
return this;
}
}
class B extends A {
bar(): this {
return this;
}
}
which will allow for
const b = new B();
b.foo().bar();
I have two examples for you, one with overloads and one with a generic interface.
If you meant for the new C().foo().zoom() version to work, you can achieve that, while still getting a warning about the bar() mistake with the following code, which creates a compatible overload that returns a subtype of the type in the parent class:
class A {
foo(): A {
return this;
}
}
class B extends A {
foo(): B {
return this;
}
bar(): B {
return this;
}
}
class C extends A {
foo(): C {
return this;
}
zoom(): C {
return this;
}
}
const result = new C().foo().zoom();
If the real method in your code actually does something you want to re-use, you can call super.foo()... but in the example code that isn't needed.
foo(): C {
const a = super.foo();
// You still need to return this, as it is a C, not an A.
return this;
}
You can't make the base class generic, in order to return a type T. You can't use a class as a type constraint on its own type argument. You also have a problem with A not being guaranteed compatible with a T that extends A.
What you could do is introduce an interface, and use it on each class:
interface Fooable<T> {
foo(): T;
}
class A {
foo(): any {
return this;
}
}
class B extends A implements Fooable<C> {
bar(): B {
return this;
}
}
class C extends A implements Fooable<C> {
zoom(): C {
return this;
}
}
const result = new C().foo().zoom();
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