Unable to type class method that extends from a abstract class generic method (TypeScript)

There are two distinct flavors of generics in TypeScript: generic functions and generic types.


A generic function declares its generic type parameter (or parameters) on the call signature of the function, like the handle method here:

interface RCHGenFunc {
  handle<T>(request: T): Promise<T>
}

The type parameter on a generic function (T above) isn’t specified until the function is actually called, at which point the caller specifies it (or the compiler infers it on behalf of the caller). This means that a generic function implementation must be able to deal with any possible specification of T that the function caller wants.


A generic type declares its generic type parameter (or parameters) on the type declaration, like the RCHGenType type here:

interface RCHGenType<T> {
  handle(request: T): Promise<T>
}

The type parameter on a generic type (T above) must be specified before you can have a value of that type. If a generic type has a method that refers to the type parameter (as in handle() above), that type parameter is fixed as soon as the surrounding generic type is specified, before that method is called. Once you are talking about, say, an RCHGenType<GreetingHandlerRequest>, then its handle() method only needs to be able to handle a GreetingHandlerRequest. And so to implement that function you do not need to deal with any possible value of T.


The distinction can feel a bit fuzzy when you have a specific type with a generic method as opposed to a generic type with a specific method. The important part is the scope of the generic. If I take generics in the type system and make an analogy with functions in JavaScript, the difference is this (the following is JS, not necessarily TS):

const rchGenFunc = () => (T) => "Promise<" + T + ">";
const rchGenType = (T) => () => "Promise<" + T + ">";

Each of them has a parameter named T, but they have different scopes. In rchGenFunc I don’t specify T when I call it; instead, I call it with no parameter and then get a function back that can accept any parameter. But in rchGenType I have to specify T when I call it, and then get back something that only works for that particular T.


Backing up to your problem: you are using a generic function but you should really be using a generic type instead. Even in your WelcomeHandler, your handle() method is generic where GreetingHandlerRequest is just a coincidentally-named type parameter, and has nothing to do with any GreeingHandlerRequest interface/class you may be importing (which is why you get “unused” warnings).

So let’s move the type parameter out of the call signature and up into the type:

interface ResponsibilityChainHandler<T> {
  setNext(handler: ResponsibilityChainHandler<T>): ResponsibilityChainHandler<T>
  handle(request: T): Promise<T>
}

And that means AbstractResponsibilityChainHandler should also be generic:

abstract class AbstractResponsibilityChainHandler<T>
  implements ResponsibilityChainHandler<T> {
  private nextHandler?: ResponsibilityChainHandler<T>

  public setNext(handler: ResponsibilityChainHandler<T>): ResponsibilityChainHandler<T> {
    this.nextHandler = handler
    return this.nextHandler
  }

  public async handle(request: T): Promise<T> {
    if (this.nextHandler) {
      return this.nextHandler.handle(request)
    }
    return request
  }
}

Finally, we can talk about WelcomeHandler. Assuming that GreetingHandlerRequest looks like this:

interface GreetingHandlerRequest {
  existingAttr: string;
}

then WeclomeHandlercan look like this:

class WelcomeHandler extends AbstractResponsibilityChainHandler<GreetingHandlerRequest> {
  public async handle(request: GreetingHandlerRequest): Promise<GreetingHandlerRequest> {
    request.existingAttr.toUpperCase();
    return super.handle(request)
  }
}

Here, WelcomeHandler is not itself a generic class. It is a specific class that you get by extending AbstractResponsibilityChainHandler<GreetingHandlerRequest>, which is a specific type you get by plugging in the specific GreetingHandlerRequest type as the type parameter T in AbstractResponsbilityChainHandler<T>.

Now the WelcomeHandler‘s handle() method accepts only GreetingHandlerRequest parameters. You don’t have to make it generic (there’s no type parameter on its declaration), and inside the implementation you have access to the specific properties of GreetingHandlerRequest because of this restriction.


If you had said that you really do want handle() to be a generic method and deal with any possible request type, then we’d have had to either abandon the class hierarchy (since a WelcomeHandler would not properly handle() all requests), or we’d have to make it accept all requests but put some kind of runtime check inside so that it does nothing unless it gets the right type of request. But that’s not what you want (thank goodness) so the above solution is probably the right one for you.

Playground link to code

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top