【问题标题】:generic extends parameter not assignable to argument type泛型扩展参数不可分配给参数类型
【发布时间】:2020-06-18 21:22:34
【问题描述】:

我试图在通用参数中使用extends 来索引对象,但出现以下错误:

“IHandlerMap[K]”类型的参数不可分配给“BasicHandler”类型的参数。 键入'基本处理程序 | KeyPressHandler' 不可分配给类型 'BasicHandler'。 类型 'KeyPressHandler' 不可分配给类型 'BasicHandler'.ts(2345)

代码:

export type BasicHandler = () => void;
export type KeyPressHandler = (keyCode: number) => void;

interface IHandlerMap {
  OnTick: BasicHandler;
  OnKeyDown: KeyPressHandler;
}

type HandlersType = {[key in keyof IHandlerMap]: IHandlerMap[key][]};

export class EventManager {
  private readonly handlers: HandlersType = {
    OnTick: [],
    OnKeyDown: [],
  };

  public addHandler<K extends keyof IHandlerMap>(
    type: K,
    handler: IHandlerMap[K]
  ): {
    this.handlers[type].push(handler); // Error is occuring here.
  }
}

似乎只有当对象属性可以是BasicHandlerKeyPressHandler时,TS才能推断出对象属性是BasicHandler类型的数组?

如何使用keyof 来索引接口 (handler: IHandlerMap[K]),然后使用该参数推送到同样为 type'd (this.handlers[type].push(handler)) 的对象键?

我在解释我想要做什么时遇到了很多麻烦,这是错误的做法吗?如果是这样,有什么替代方法?

【问题讨论】:

  • 有趣的是,我现在没有看到任何其他方法,除了断言像 any 这样的不健全类型。我希望@jcalz 会检查一下。

标签: typescript generics types typescript-typings typescript-generics


【解决方案1】:

我的猜测是,this.handlers[type] 的表达式类型会触发你的错误:

addHandler 函数体内我们不知道"OnTick""OnKeyDown" 属性type 中的哪一个具有,因此假定其基类型keyof IHandlerMap"OnTick" | "OnKeyDown" 用于属性访问.属性访问this.handlers[type] 将解析为HandlersType[keyof IHandlerMap] 类型,即BasicHandler[] | KeyPressHandler[]

现在当我们调用this.handlers[type].push 时,push 方法可以用BasicHandler[]KeyPressHandler[] 调用,所以它的类型是两个push 方法的联合

| (...items: BasicHandler[]): number 
| (...items: KeyPressHandler[]): number

从 TS 3.3 开始,您可以实际上调用union type of functions,但它们的参数会相交 (&amp;)。 push 签名看起来像这样:

(method) Array<T>.push(items: (BasicHandler & KeyPressHandler)[]): number

BasicHandler没有函数参数,KeyPressHandler一个。所以交集导致......没有预期的参数。您可以将s: string 之类的参数添加到BasicHandler,它会变得更加明显(然后将鼠标悬停在push 上)。

我们能做什么?

this.handlers 使用类型为HandlersTypeMixed 的额外变量,或直接将其用于this.handlers。注意:(BasicHandler | KeyPressHandler)[]!==BasicHandler[] | KeyPressHandler[]。只有前者不会导致联合函数类型并允许pushPlayground

type HandlersTypeMixed = { [key in keyof IHandlerMap]: (BasicHandler | KeyPressHandler)[] }

public addHandler<K extends keyof IHandlerMap>(
    type: K,
    handler: IHandlerMap[K]
) {
    const hs: HandlersTypeMixed  = this.handlers
    hs[type].push(handler);
}

【讨论】:

  • 标记为答案。注意其他可能面临类似情况的人:添加一个额外的变量似乎是比直接使用它更好的方法,当我们调用处理程序数组中的函数时直接使用,我们需要正确地对函数进行类型转换。通过使用额外的变量,我们不必担心类型转换处理函数。
猜你喜欢
  • 2019-06-24
  • 2021-06-14
  • 1970-01-01
  • 2018-08-10
  • 2012-07-24
  • 2021-10-17
  • 1970-01-01
  • 2021-02-03
相关资源
最近更新 更多