typescriptaxiosopenapi-generator

function with typeof parameter returning instance of the given type


I would like to make a factory method for all my Axios Api classes (generated by OpenAPI generator).

So Open-API generates me a BaseAPI class with a constructor and dozen of Api classes extending this BaseAPI corresponding to my Backend Controllers.

So i tried that function:

import { BaseAPI } from "@/api/base";

abstract class AbstractControllerHandler {
  protected createApi<T extends typeof BaseAPI>(clazz: T)  {
    return new clazz(
      undefined,
      "",
      AxiosInstanceHolder.getInstance().getAxiosInstance(),
    );
  }

Which is used by this other class:

import { CrudCapitalsourceControllerApi } from "@/api";

class CapitalsourceControllerHandler extends AbstractControllerHandler {
  private api: CrudCapitalsourceControllerApi;

  public constructor() {
    super();

    this.api = super.createApi(CrudCapitalsourceControllerApi);
  }
}

CrudCapitalsourceControllerApi is defined as class CrudCapitalsourceControllerApi extends BaseAPI and is generated by OpenAPI.

I'm getting a ts(2739) basically stating that createApi returns type BaseAPI which cannot be assigned to a variable of type CrudCapitalsourceControllerApi.

I guess I have to specify a return type for createApi to get it to work, but T as return type does not work. It raises a ts(2322) and I don't return a type I return an instance... so any idea of how I can fix this? I guess I would need something as "instanceof T" for the return type declaration


Solution

  • I'm going to shorten your code to just the real issue here, since your code is missing lots of things that would allow me to run it.

    class Foo { baseAPI!: true }
    class Bar extends Foo { foo!: true }
    
    function createApi<T extends typeof Foo>(clazz: T) {
      return new clazz();
    }
    
    const bar: Bar = createApi(Bar) // type error because Foo is not assignable to Bar
    

    The problem here is that you don't actually care about the constructor, you care about what it constructs. So that should be your generic parameter T.

    So if you change createApi to this:

    function createApi<T extends Foo>(clazz: new () => T) {
      return new clazz();
    }
    

    Then you get no type error here:

    const bar: Bar = createApi(Bar) // fine
    

    See plauground