Search code examples
typescripttypescript-generics

How to select a type for a method based on the generic type provided in the class in TypeScript?


I have the following class:

class Processor<TReq, TRes> {
    constructor(
        private handler: (input: TReq) => TRes
    ) {}

    public handleRequest(input: TReq): TRes {
       ...
       const res = this.handler(input);
       ...
    }
}

In this design, I can do this:

const myProcessor = new Processor<string, string>(
    (input: string) => `Hello ${input}`
);
myProcessor.handleRequest('Hello');

My issue is that I want to be able to pass never to TReq, and when that happens, I would like handleRequest to be of type () => TRes, and handler as well:

const myProcessor = new Processor<never, string>(
    () => 'Hi'
);
myProcessor.handleRequest();

How can I achieve this?


Solution

  • I think the closest you can get is the following

    1. You need to define generic constraint for TReq
    2. You need to make handleRequst argument optional
    class Processor<TReq, TRes> {
      private handler: [TReq] extends [never] ? () => TRes : (input: TReq) => TRes;
    
      constructor(
        handler: [TReq] extends [never] ? () => TRes : (input: TReq) => TRes,
      ) {
        this.handler = handler;
      }
    
      public handleRequest(): TReq extends never ? TRes : never;
      public handleRequest(input: TReq): TReq extends never ? never : TRes;
      public handleRequest(input?: TReq): TRes {
        const res =
          typeof input === 'undefined'
            ? (this.handler as () => TRes)()
            : (this.handler as (input: TReq) => TRes)(input);
        return res;
      }
    }
    

    upd: added handleRequest overloads with generic constraint as suggested in comments. Still not perfect, but i'm afraid this is the best you can get from TS