gpt4 book ai didi

javascript - 如何在 TypeScript 中包装的 addEventListener 调用中正确键入事件处理程序

转载 作者:行者123 更新时间:2023-12-01 16:10:35 31 4
gpt4 key购买 nike

我正在尝试在 addEventListener 上编写抽象层方法,但我遇到了打字问题。在下面的最小复制中;

function addEventListener<T extends EventTarget>(
element: T,
type: string,
handler: EventListener
) {
element.addEventListener(type, handler);
}

addEventListener<Window>(window, "mousedown", (event: MouseEvent) => {
console.log(event.pageX);
});


TypeScript 提示参数类型与 MouseEvent 不兼容:
Argument of type '(event: MouseEvent) => void' is not assignable to parameter of type 'EventListener'.
Types of parameters 'event' and 'evt' are incompatible.
Type 'Event' is missing the following properties from type 'MouseEvent': altKey, button, buttons, clientX, and 20 more.

我知道以下(基本)事件监听器:

window.addEventListener('mousedown', (event: MouseEvent) => {
console.log(event.pageX);
});

工作得很好,所以我假设输入 handler 有问题参数为 EventListener ,但我不知道它应该是什么。我遇到的大多数答案似乎都是针对 React 的,这与我的情况无关。

以上代码片段: TypeScript Playground | CodeSandbox

最佳答案

问题是您的 addEventListener()打字不知道type参数与 Event 的子类型相关那handler应该接受。在 TypeScript 标准库文件中 lib.dom.d.ts , 个人 EventTarget类型从 type 得到一个非常具体的映射。至handler .例如,WindowaddEventListener 的接口(interface)签名方法looks like this :

addEventListener<K extends keyof WindowEventMap>(
type: K,
listener: (this: Window, ev: WindowEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
): void;

在哪里 WindowEventMapdefined here作为 type 之间的大型映射姓名和 Event类型。对于 mousedown具体来说, property value type is MouseEvent .因此,当你打电话
window.addEventListener('mousedown', (event: MouseEvent) => {
console.log(event.pageX);
});

一切正常,因为 K推断为 "mousedown"因此 handler参数需要 MouseEvent .

如果没有这种映射信息,类型往往看起来像 typestringlistener(event: Event)=>void .但是,正如您所见,您不能简单地传递 (event: MouseEvent)=>void到期望 (event: Event)=>void 的参数,因为这样做通常不安全。如果你给我一个只接受 MouseEvent的函数s,我不能将它用作接受所有 Event 的函数s。如果我尝试,并且您给我的功能访问类似 pageX属性,我可能会收到运行时错误。

这种对函数参数的限制是在 TypeScript 2.6 中作为 --strictFunctionTypes compiler option 添加的。 .如果您关闭该编译器选项,您的错误应该会消失,但我不建议您这样做。

相反,如果您只想为您的 addEventListener 放松输入。函数,您可以在 Event 中使其通用子类型,像这样:
function addEventListener<T extends EventTarget, E extends Event>(
element: T, type: string, handler: (this: T, evt: E) => void) {
element.addEventListener(type, handler as (evt: Event) => void);
}

然后你的代码将编译:
addEventListener(window, "mousedown", (event: MouseEvent) => {
console.log(event.pageX);
});

但请记住:这种类型太松散而无法捕获错误,例如:
addEventListener(document.createElement("div"), "oopsie",
(event: FocusNavigationEvent) => { console.log(event.originLeft); }
);

如果您要直接在 HTMLDivElement 上尝试你会得到一个错误:
document.createElement("div").addEventListener("oopsie",
(event: FocusNavigationEvent) => { console.log(event.originLeft); }
); // error! oopsie is not acceptable

直到您修复了 type范围
document.createElement("div").addEventListener("blur",
(event: FocusNavigationEvent) => { console.log(event.originLeft); }
); // error! FocusNavigationEvent is not a FocusEvent

并使用了匹配的 Event亚型
document.createElement("div").addEventListener("blur",
(event: FocusEvent) => { console.log(event.relatedTarget); }
); // okay now

所以,这就是我能到这里的程度。您可以尝试从所有已知的 EventTarget 中制作一个巨大的映射。输入所有正确的 type每个目标的参数,然后到所有正确的 Event每个 EventTarget 的子类型/ type对,并有一个通用 addEventListener有效的功能...但它的大小与 lib.dom.d.ts 相当文件,我不想在这上面花太多时间。如果您需要类型安全,最好不要使用这种特定的抽象。否则,如果您对编译器缺少一些不匹配感到满意,那么上述类型可能是一种可行的方法。

好的,希望有帮助;祝你好运!

Playground link

关于javascript - 如何在 TypeScript 中包装的 addEventListener 调用中正确键入事件处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59940863/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com