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