Typescript 类型为'的参数;(事件:BlahEvent)=>;无效';不可分配给类型为';EventListenerOrEventListenerObject';

Typescript 类型为'的参数;(事件:BlahEvent)=>;无效';不可分配给类型为';EventListenerOrEventListenerObject';,typescript,typescript3.0,Typescript,Typescript3.0,我已经阅读并尝试了在中找到的建议,但未能明智地将其应用于我的情况 我已经接手了一个TS2.0的项目,我希望能用它升级到3.1。我已经阅读了有关标题中错误的内容,并大致了解了它发生的原因,但我的打字脚本知识在这一点上是有限的 我有以下课程: EventTargetImpl似乎是一个用于管理事件侦听器集合的类 class EventTargetImpl implements EventTarget { private eventListeners: any = {}; public

我已经阅读并尝试了在中找到的建议,但未能明智地将其应用于我的情况

我已经接手了一个TS2.0的项目,我希望能用它升级到3.1。我已经阅读了有关标题中错误的内容,并大致了解了它发生的原因,但我的打字脚本知识在这一点上是有限的

我有以下课程:

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);
    }
}
…但除此之外,除了扩展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); 
        });
我链接了一个我已经看过的答案;我喜欢jcalz的建议,即我可以在本地事件中声明合并为InputEvent类型,然后使用addEventHandler的强类型重载,而不是严格类型重载,但我无法确定在我拥有的对象/继承层次结构中在何处/如何执行此操作

我还尝试了FrederikKrautwald的建议,将
作为(e:Event)=>void)
添加到内联函数的末尾,但TypeScript更改为

将类型“(event:InputEvent)=>void”转换为类型“(e:event)=>void”可能是错误的,因为两种类型都没有充分重叠

JacobEvelyn建议的一种修改形式最像我在C#中所做的,如果我在泛型中有一个特定类型:

inputWrapper.addEventListener(InputEvent.TYPE_UP, (event: Event) => {
   let iv: InputEvent = (event as InputEvent); 
   this.handleMouseUp(iv.coords.x, iv.coords.y); 
});
但是我再次得到了“两种类型都没有与另一种类型充分重叠”的错误

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

下面是一个给出您遇到的错误的一般示例

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
类型从
t
扩展到
unknown
,然后从
unknown
缩小到
U
类型。每个步骤都是允许的,但不能跳过中间的步骤。(我不能说“这只猫是一只狗”,但我可以说“这只猫是一只狗的动物”。)

您还可以使用其他可能的中间类型;它必须是两个不相关类型的公共超类型或公共子类型:

  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和U一样,因为
unknown
仅在TypeScript 3.0中出现),所以使用它时不要感到太糟糕。麻烦的本质主要是迫使你思考你在做什么:你确定这个值可以被视为这种类型吗?如果不是,那么其他一些解决方案将比类型断言更好。如果是这样,那就继续吧


希望有帮助;祝你好运

是否确实可以将此值视为此类型?-哈。没有。但是编写原始代码(并且成功)的人是。我的主要问题似乎是其他人的问题:创建事件的自定义派生,为它们添加处理程序,并因此将一个函数作为参数传递给超类型的addEventListener。我有点难以理解为什么像
addThing(f(z:ZParent))
这样的方法不能接受
addThing(f(y:ZChild))
但我只是在挣扎,因为我一直认为javascripty语言在这类事情上相当快速和松散——我不希望能够将一个以子类实例为参数的委托传递给要求一个以c#为基类的委托的对象。。因此,我想知道我的父类(EventTargetImpl)的特定子类(InputWrapper)是否应该重新定义一个
addThing(f(y:ZChild))
将ZChild框为ZParent并调用基addThing我猜TLDR我的问题是“我应该如何正确地做到这一点?我有一个EventImpl和EventTargetImpl,我的事件和它们的提升类从中扩展,我应该如何让我的特定子类在这个事件处理程序的通用基列表中正确地存储它的特定事件处理程序?我不认为
EventTarget.addEventHandler()标准TypeScript库中的
签名允许您根据事件名称的值缩小事件处理程序回调的类型,就像
元素和
窗口所做的那样。可能您只想在任何地方使用
InputEvent
而不是
Event
HTMLInputElement
而不是
EventTarget
。或者您可以使用其他问题的链接答案中提到的类型保护或类型断言(如果类型断言给你带来麻烦,这个答案应该会有帮助。如果
addThing
想要一个接受
ZParent
的函数,而你给它一个接受
ZChild
的函数,那么你并没有给它想要的。这是因为它是一个参数类型。直觉上,如果我告诉你给我一个参数的电话号码接受艾尔的兽医
function assert<T, U>(t: T): U {
  return t as unknown as U; // works
}
  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