泛型类方法和泛型类之间的打字稿语义差异

时间:2018-08-14 06:12:31

标签: typescript generics

我对为什么下面的示例代码在使用类泛型而不是方法泛型时起作用的原因感到困惑。

首先,我有简单的RequestResponse类:

abstract class BaseRequest {
  baseProperty: string;
}

class ChildRequest extends BaseRequest {
  childProperty: string;
}

abstract class BaseResponse<R extends BaseRequest> {
  data: any;
  request: R;
}

class ChildResponse extends BaseResponse<ChildRequest> {
}

为了简化继承,我使用下面的泛型方法代码(请注意,该类不是泛型的,仅方法和扩展基类不需要泛型):

abstract class RequestClient {
  abstract request<RQ extends BaseRequest, RP extends BaseResponse<RQ>>(request: RQ): RP;
}


class ChildClient extends RequestClient {
  request(request: ChildRequest): ChildResponse {
    return undefined;   // just testing the code structure right now
  }
}

但是编译器在ChildClient.request上进行补偿:

TS2416: Property 'request' in type 'ChildClient' is not assignable to the same property in base type 'RequestClient'.
  Type '(request: ChildRequest) => ChildResponse' is not assignable to type '<RQ extends BaseRequest, RP extends BaseResponse<RQ>>(request: RQ) => RP'.
    Types of parameters 'request' and 'request' are incompatible.
      Type 'RQ' is not assignable to type 'ChildRequest'.
        Type 'BaseRequest' is not assignable to type 'ChildRequest'.
          Property 'childProperty' is missing in type 'BaseRequest'.

但是,使用类泛型可以正常工作:

abstract class RequestClient<RQ extends BaseRequest, RP extends BaseResponse<RQ>> {
  abstract request(request: RQ): RP;
}

class ChildClient extends RequestClient<ChildRequest, ChildResponse> {
  request(request: ChildRequest): ChildResponse {
    return undefined;
  }
}

我想避免使用类泛型,因为它开始创建类继承trick滴爆炸式增长,最终我得到大量无用的子类,只是为了使泛型方法正确编译。

为什么类泛型有效,但方法泛型无效?这里的语义有什么区别?

谢谢!

1 个答案:

答案 0 :(得分:3)

因为在通用方法示例中,您的子类违反了基类定义的协定。

基类承诺该类的实例允许调用者选择任何类型的请求和响应,但是子类重写请求方法并限制合同:不,我只能发送一个ChildRequest,您将只取回一个ChildResponse。

考虑以下情况:

const client: RequestClient = new ChildClient();

这是有效的,因为ChildClient扩展了RequestClient。因此,由于客户端是RequestClient,所以您可以

const response: MyResponse = client.request<MyRequest, MyResponse>(new MyRequest());

如果编译器接受您想要的内容,将调用哪个方法?会发生什么?