无法键入从抽象 class 泛型方法扩展的 class 方法 (TypeScript)

Unable to type class method that extends from a abstract class generic method (TypeScript)

所以我有一个实现接口的抽象class:

export default interface ResponsibilityChainHandler {
  setNext(handler: ResponsibilityChainHandler): ResponsibilityChainHandler

  handle<T>(request: T): Promise<T>
}
import ResponsibilityChainHandler from './responsibility-chain.handler.interface'

export default abstract class AbstractResponsibilityChainHandler
  implements ResponsibilityChainHandler {
  private nextHandler?: ResponsibilityChainHandler

  public setNext(handler: ResponsibilityChainHandler): ResponsibilityChainHandler {
    this.nextHandler = handler
    return this.nextHandler
  }

  public async handle<T>(request: T): Promise<T> {
    if (this.nextHandler) {
      return this.nextHandler.handle(request)
    }
    return request
  }
}

现在,我想定义一个新的 class,它只接受类型为 GreetingHandlerRequest 的特定对象(定义为 class)。

import AbstractResponsibilityChainHandler from '../../domain/responsibility-chain.handler.abstract'
import GreetingHandlerRequest from '../../domain/greeting-handler-request.domain';

export default class WelcomeHandler extends AbstractResponsibilityChainHandler {
  public async handle<GreetingHandlerRequest>(
    request: GreetingHandlerRequest
  ): Promise<GreetingHandlerRequest> {
    return super.handle(request)
  }
}

但是,我在 GreetingHandlerRequest 导入时遇到以下错误:

'GreetingHandlerRequest' is declared but its value is never read.

如果我尝试在方法内部做一些事情,比如 request.existingAttr 它会抱怨:

Property 'existingAttr' does not exist on type 'GreetingHandlerRequest'.

但是 class GreetingHandlerRequest 有一个可公开访问的属性,名为 existingAttr

我也尝试过 import type,但没有成功。我已经被困了一段时间了。问了一些伙伴和谷歌搜索对我来说没有结果。有什么想法吗?

TypeScript 中有两种不同风格的 generics:泛型 functions 和泛型 types.


泛型函数在函数的调用签名上声明其泛型类型参数(或多个参数),例如此处的 handle 方法:

interface RCHGenFunc {
  handle<T>(request: T): Promise<T>
}

泛型函数(上面的T)的类型参数直到函数被实际调用时才会被指定,此时调用者指定它(或者编译器代表调用者推断它) .这意味着通用函数实现必须能够处理函数调用者想要的任何可能的 T 规范。


一个泛型 类型 在类型声明中声明了它的泛型类型参数(或多个参数),例如这里的 RCHGenType 类型:

interface RCHGenType<T> {
  handle(request: T): Promise<T>
}

泛型类型(上面的 T)的类型参数必须先指定,然后才能获得该类型的值。如果泛型类型有一个引用类型参数的方法(如上面的 handle() 中),则在调用该方法之前,一旦指定了周围的泛型类型,该类型参数就会被固定。一旦您谈论 RCHGenType<GreetingHandlerRequest>,那么它的 handle() 方法只需要能够处理 GreetingHandlerRequest。因此,要实现该功能,您不需要处理 T.

的任何可能值

当您拥有具有泛型方法的特定类型而不是具有特定方法的泛型类型时,区别可能会有点模糊。重要的部分是通用的范围。如果我拿类型系统中的泛型类比JavaScript中的函数,区别是这样的(以下是JS,不一定是TS):

const rchGenFunc = () => (T) => "Promise<" + T + ">";
const rchGenType = (T) => () => "Promise<" + T + ">";

他们每个人都有一个名为T的参数,但他们有不同的范围。在 rchGenFunc 中,我在调用它时没有指定 T ;相反,我不带参数调用它,然后返回一个可以接受任何参数的函数。但是在 rchGenType 中,我必须在调用它时指定 T,然后取回仅适用于该特定 T.

的内容

支持您的问题:您正在使用泛型函数,但实际上您应该使用泛型类型。即使在您的 WelcomeHandler 中,您的 handle() 方法也是通用的,其中 GreetingHandlerRequest 只是一个 coincidentally-named 类型参数 ,与任何事情无关对于任何 GreeingHandlerRequest interface/class 您可能正在导入(这就是您收到“未使用”警告的原因)。

所以让我们将类型参数移出调用签名并向上移动到类型中:

interface ResponsibilityChainHandler<T> {
  setNext(handler: ResponsibilityChainHandler<T>): ResponsibilityChainHandler<T>
  handle(request: T): Promise<T>
}

这意味着 AbstractResponsibilityChainHandler 也应该是通用的:

abstract class AbstractResponsibilityChainHandler<T>
  implements ResponsibilityChainHandler<T> {
  private nextHandler?: ResponsibilityChainHandler<T>

  public setNext(handler: ResponsibilityChainHandler<T>): ResponsibilityChainHandler<T> {
    this.nextHandler = handler
    return this.nextHandler
  }

  public async handle(request: T): Promise<T> {
    if (this.nextHandler) {
      return this.nextHandler.handle(request)
    }
    return request
  }
}

终于可以聊聊WelcomeHandler了。假设 GreetingHandlerRequest 看起来像这样:

interface GreetingHandlerRequest {
  existingAttr: string;
}

然后 WeclomeHandler可以看起来像这样:

class WelcomeHandler extends AbstractResponsibilityChainHandler<GreetingHandlerRequest> {
  public async handle(request: GreetingHandlerRequest): Promise<GreetingHandlerRequest> {
    request.existingAttr.toUpperCase();
    return super.handle(request)
  }
}

这里,WelcomeHandler 本身不是泛型 class。它是通过扩展 AbstractResponsibilityChainHandler<GreetingHandlerRequest> 获得的特定 class,这是通过插入特定 GreetingHandlerRequest 类型作为类型参数 T 在 [=] 中获得的特定类型45=].

现在 WelcomeHandlerhandle() 方法只接受 GreetingHandlerRequest 参数。您不必将其设为通用(其声明中没有类型参数),并且在实现内部您可以访问 GreetingHandlerRequest 的特定属性,因为此限制。


如果您曾说过您确实希望 handle() 成为通用方法并处理任何可能的请求类型,那么我们将不得不放弃 class 层次结构(因为WelcomeHandler 不会正确地 handle() 所有请求),或者我们必须让它接受所有请求,但在里面放置某种运行时检查,这样它就什么都不做,除非它得到正确类型的请求.但这不是您想要的(谢天谢地),因此上述解决方案可能适合您。

Playground link to code