typescript - '(event: BlahEvent) => void' 类型的参数不能分配给 'EventListenerOrEventListenerObject' 类型的参数

标签 typescript typescript3.0

我已经阅读并尝试了 Argument of type '(e: CustomEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject' 中的建议但还没有能够明智地将它们应用于我的情况

我已经接手了一个 TS2.0 项目,我希望用它迁移到 3.1。我已经阅读了关于标题中的错误并有点理解它为什么会发生,但我的 typescript 知识在这一点上是有限的

我有这些类(class):

EventTargetImpl 似乎是一个管理事件监听器集合的类

class EventTargetImpl implements EventTarget {
    private eventListeners: any = {};

    public addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void {
        if (!this.eventListeners.hasOwnProperty(type))
            this.eventListeners[type] = [];
        this.eventListeners[type].push(listener);
    }

    public dispatchEvent(event: Event): boolean {
        var type = event.type;
        if (!this.eventListeners.hasOwnProperty(type)) {
            this.eventListeners[type] = [];
        }
        for (var i = 0; i < this.eventListeners[type].length; i++) {
            this.eventListeners[type][i].call(this, event);
        }
        return true;
    }

    public removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void {
        if (!this.eventListeners.hasOwnProperty(type))
            return;
        var listenerIndex = this.eventListeners[type].indexOf(listener);
        if (listenerIndex === -1)
            return;
        this.eventListeners[type].splice(listenerIndex, 1);
    }
}

InputEvent 类定义事件名称的常量字符串,它们是某种本地版本的事件。
class InputEvent extends EventImpl
{
    public static TYPE_UP = "up";
    constructor(type: string) {
        super(type);
    }
}

..但除此之外,除了扩展 EventImpl 之外,似乎没有做太多事情:
class EventImpl implements Event
{
    public bubbles: boolean;
    public cancelBubble: boolean;
    public cancelable: boolean;
    public currentTarget: EventTarget;
    public defaultPrevented: boolean;
    public eventPhase: number;
    public isTrusted: boolean;
    public returnValue: boolean;
    public srcElement: Element;
    public target: EventTarget;
    public timeStamp: number;
    public type: string;
    public initEvent(eventTypeArg: string, canBubbleArg: boolean, cancelableArg: boolean): void { throw "not implemented";}
    public preventDefault(): void {
        this.defaultPrevented = true;
    }
    public stopImmediatePropagation(): void { throw "not implemented"; }
    public stopPropagation(): void { throw "not implemented";}
    public AT_TARGET: number;
    public BUBBLING_PHASE: number;
    public CAPTURING_PHASE: number;
    constructor(type: string) {
        this.type = type;
    }
}

输入包装器。这个类似乎拥有/持有一个 HTML 元素,并且要么为该元素的事件注册标准处理程序,要么用 InputEvent 中命名的本地类型事件的附加到自身的处理程序替换它们
class InputWrapper extends EventTargetImpl {

    constructor(private element: HTMLElement) {
        super();
        this.attachListeners();
    }

    private attachListeners(): void {
        var detachGlobalListeners: () => void;
        var attachGlobalListeners: () => void;

        var mouseUpEventHandler = (event) => {
            var point = this.normaliseMouseEventCoords(event);
            this.handleMouseUp(point.x, point.y, event, InputType.Mouse);
            detachGlobalListeners();
        };

        this.element.addEventListener("mouseup", (event) => {
            if (!this.globalMode) {
                this.handleMouseUp(event.offsetX, event.offsetY, event, InputType.Mouse);
            }
        });

        detachGlobalListeners = () => {
            this.globalMode = false;
            window.removeEventListener("mouseup", mouseUpEventHandler);
        };

        attachGlobalListeners = () => {
            this.globalMode = true;
            window.addEventListener("mouseup", mouseUpEventHandler);
        };
    }

    private handleMouseUp(x: number, y: number, event: Event, inputType: InputType): void {
        event.stopPropagation();
        event.preventDefault();
        this.dispatchEvent(new InputEvent(InputEvent.TYPE_UP, { x: x, y: y }, inputType));
    }
}

这是使用它的地方,也是我收到错误的地方。我在上面的代码行中的 dispatchEvent 调用中也遇到了类似的错误:
        var inputWrapper = new InputWrapper(this.foreground);
        inputWrapper.addEventListener(InputEvent.TYPE_UP, (event: InputEvent) => { 
          this.handleMouseUp(event.coords.x, event.coords.y); 
        });

我链接了一个我已经看过的 SO 答案;我喜欢 jcalz 的建议,即我可以在我的本地事件中声明合并为 InputEvent 类型,然后使用 addEventHandler 的强类型重载,而不是字符串类型的重载,但我无法弄清楚在哪里/如何做到这一点我拥有的对象/继承层次结构

我也尝试了 FrederikKrautwald 的建议 as (e: Event) => void)到内联函数的末尾,但 TypeScript 更改为提示

Conversion of type '(event: InputEvent) => void' to type '(e: Event) => void' may be a mistake because neither type sufficiently overlaps with the other



JacobEvelyn 建议的一种修改形式最像我在 C# 中所做的那样,如果我将特定类型装箱在通用类型中:
inputWrapper.addEventListener(InputEvent.TYPE_UP, (event: Event) => {
   let iv: InputEvent = (event as InputEvent); 
   this.handleMouseUp(iv.coords.x, iv.coords.y); 
});

但是我再次收到“两种类型都与另一种类型没有充分重叠”的错误

最佳答案

如何解决“两种类型都与另一种类型不充分重叠”错误
初步警告:type assertions一般来说,不是类型安全的。编译器通常不让您做某事,因为您做这些事是错误的。但是,也有很多情况您确定某些东西是安全的,但编译器不相信。并且您可以使用类型断言来强制编译器接受您的真实版本。这一切都很好,除非您碰巧错了,在这种情况下,您可以期待疯狂的运行时行为,您不应该责怪试图警告您的糟糕编译器。然而,编译器不像人类那么聪明,所以断言通常是必要的。从现在开始,我假设您想要做出的断言足够安全。
这是给出您遇到的错误的一般示例。

function assert<T, U>(t: T): U {
  return t as U; // error
  // [ts] Conversion of type 'T' to type 'U' may be a mistake because 
  // neither type sufficiently overlaps with the other. If this was 
  // intentional, convert the expression to 'unknown' first.
}
我们试图断言 T 类型的值类型为 U ,我们得到了“两种类型都没有充分重叠”的错误。基本上类型断言可用于将类型扩展为父类(super class)型,这总是安全的,因为您更通用(每只狗都可以正确地称为动物);并且它们还可用于将类型缩小为更具体的类型,这是不安全的,因为您更具体(并非每种动物都可以正确地称为狗)......这种缺乏类型安全是故意的,并且我们首先有断言的原因。但是在 TypeScript 中,类型断言不能用于直接从一种类型转换为既不窄也不宽的不相关类型(所以我可以直接断言猫是动物,我可以直接断言动物是狗,但是我不能直接断言猫是狗)。
解决这个问题的关键也在报错信息中提到:“先将表达式转换为unknown”:
function assert<T, U>(t: T): U {
  return t as unknown as U; // works
}
错误消失了,因为我们已经扩大了 t从类型 T一直到top type unknown ,然后从 unknown 缩小下至类型 U .每个步骤都是允许的,但您不能跳过中间步骤。 (我不能说“这只猫是一只狗”,但我可以说“这只猫是一只属于狗的动物”。)
您可以使用其他可能的中间类型;它只需要是两个不相关类型的公共(public)父类(super class)型或公共(public)子类型:
  return t as unknown as U; // okay, widen T to top type unknown, and narrow unknown to U
  return t as (T | U) as U; // okay, widen T to (T | U), and narrow (T | U) to U

  return t as never as U; // okay, narrow T to bottom type never, and widen never to U
  return t as (T & U) as U; // okay, narrow T to (T & U), and widen (T & U) to U

  return t as any as U; // okay, any is both top-like and bottom-like, 
  // so you are, uh, "narrowidening" it through any 
这个中间人断言是一种相当普遍的做法(尤其是 t as any as U,因为 unknown 在 TypeScript 3.0 中只是 introduced),所以不要对使用它感到太糟糕。麻烦的本质主要是迫使您考虑自己在做什么:您确定可以将此值视为这种类型吗?如果没有,其他一些解决方案会比类型断言更好。既然如此,那就继续吧!
希望有所帮助;祝你好运!

关于typescript - '(event: BlahEvent) => void' 类型的参数不能分配给 'EventListenerOrEventListenerObject' 类型的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53833908/

相关文章:

angular - 用 typescript 编写的字符串未正确编码

typescript - protobuf.js pbts : Generating typescript types from . 原型(prototype)不带 null |不明确的

typescript 删除可选属性

typescript - 如何清理 typescript 缓存?

typescript - 在 Ionic 中应用幻灯片

javascript - 为 Google 的 Prettify 创建自己的规则的最先进方法是什么?

javascript - 默认为 MomentJS/Date 对象 UTC

typescript - 条件/三元结果在 typescript 中未正确进行类型检查?

typescript - 强制对象至少有一个键( Object.keys(o).length > 0 )

angular - 在 Angular 7( typescript 3.1.3)中找不到名称 'require'