How do I overload generic methods to make them les

2019-07-15 22:08发布

问题:

I have a generic static method in an abstract class:

abstract class Base {
  static find<T extends Base>(options?: Object): Promise<T[]> {
    return findResults(options);
  }
}

And I want to specify it's type in an derived class:

class Extended extends Base { 
  static find<Extended>(options?: Object): Promise<Extended[]>;
  // Error: Function implementation is missing or not immediately following the declaration.
}

Is this behavior supported in any way by Typescript?

回答1:

That type seems to violate the Liskov substitution principle for the static side of Extended. Apparently the Extended constructor needs to be a valid Base constructor, so if Base.find() can return a Promise<T[]> for any T that extends Base, then Extended.find() should be able to do that too.

Generics in static methods are weird, since static members can't access the type parameters of the class. But maybe what you want isn't really generic at all... you want Base.find() to return Promise<Base[]>, and Extended.find() to return Promise<Extended[]>, right? That sounds like a motivating example for polymorphic this. Unfortunately polymorphic this isn't supported for static members, at least as of yet.

My suggestion would be to just manually implement the relationship you expect:

abstract class Base {
  static find(options?: Object): Promise<Base[]> {
    return findResults(options); // is this some function somewhere?
  }
}

class Extended extends Base {
  // maybe not type safe 
  static find: (options?: Object) => Promise<Extended[]>;
}

Note that all I've done there is declare that Extended.find() returns a Promise<Extended[]> without changing its implementation from Base.find(). That might not be type safe, unless the implementation of Base.find() is smart enough to know how to do this. I kind of doubt it, so be careful. You may instead want to implement it differently:

class Extended extends Base {
  static find(options?: Object): Promise<Extended[]> {
    // change the implementation in a way that constrains the results,
    // maybe with a type guard somewhere
  }
}

Okay, hope that helps. Good luck!