Typescript -- instantiate child from a static method in base class, with args and referencing other static method

327 views Asked by At

I am trying to instantiate a child class from a static method in a base class. I want to type my base class correctly rather than using any as the return type on all my static methods. I tried the solution here but it doesn't work for a static method that references other static methods, or accepts arguments. How can I correctly inherit from a base class in typescript, and still reference other methods and accept arguments?

class BaseClass {
  id: string;

  [key: string]: unknown;

  static getName() {
    return this.name.toUpperCase()
  }

  static async find<T extends BaseClass>(this: new (...args: any[]) => T, id: string)
  : Promise<T> {
    const tableName = this.getName();

    const result: GetResult = db.find(tableName, id);

    return new this(result);
  }
}

class Child extends BaseClass {
  name: string;

  static findOne(id: string): Promise<Child> {
    return this.find(id);
  }
}

Child.find('abcd');

This causes two different errors

  1. Property 'getName' does not exist on type 'new (...args: any[]) => T'. (in the find method)
  2. Type 'BaseModel' is missing the following properties from type 'Child': name. (in the return type for findOne method)
1

There are 1 answers

10
Artyom Kozhemiakin On BEST ANSWER

In find method of the base class you should specify that it expects child class to implement static getName method, like this:

static async find<T>(this: { new (arg: GetResult): T } & typeof BaseClass, id: string): Promise<T>

Particularly { new (arg: GetResult): T } brings you constructor and typeof BaseClass brings you static members.

I mocked some missing parts and it typechecks.

type GetResult = string;

const db = {
    find: (a: string, b: string) => "bar",
}

class BaseClass {
  id: string = "bzzzz";

  [key: string]: unknown;

  static getName() {
    return 'NAME'
  }

  static async find<T>(this: { new (arg: GetResult): T } & typeof BaseClass, id: string)
  : Promise<T> {
    const tableName = this.getName();

    const result: GetResult = db.find(tableName, id);

    return new this(result);
  }
}

class Child extends BaseClass {
  name: string = "Child";

  static findOne(id: string): Promise<Child> {
    return this.find(id);
  }
}

Child.find('abcd');