【问题标题】:How to use the EventListener Type in React?如何在 React 中使用 EventListener 类型?
【发布时间】:2022-01-10 16:39:47
【问题描述】:

React 在一个名为 global.d.ts 的文件中定义了一些(不是全部)DOM 类型,这会导致问题。在 React 项目中使用 EventListener 类型(来自 TypeScript 库 dom)时似乎存在冲突,因为大多数其他类型都是由 React 声明的。

有没有办法在 React 项目中使用 EventListener 类型?我正在使用 next.js,所以在 tsconfig.json 中使用 exclude 丢弃 global.d.ts 文件以使 SSR 工作可能不是一个好主意?

预期内容(纯 TS):
https://codesandbox.io/s/nice-tdd-xnkov?file=/src/index.ts

window.onload = () => {
  // NO Problem: Listener is correct.

  // Explanation:
  // EventListener is coming from typescript (lib.dom.d.ts)
  // MouseEvent is coming from typescript (lib.dom.d.ts)
  const listener: EventListener = (ev: MouseEvent) => {
    console.log("Click!");
  };
  document.body.addEventListener("click", listener);
};

在 React 中不起作用:
https://codesandbox.io/s/react-dom-type-conflict-1y06r?file=/src/App.tsx

useEffect(() => {
  // Problem: Listener is seemingly incorrect.

  // Explanation:
  // EventListener is coming from typescript (lib.dom.d.ts)
  // MouseEvent is coming from react (global.d.ts)
  const listener: EventListener = (ev: MouseEvent) => {
    console.log("Click!");
  };
  document.body.addEventListener("click", listener);
}, []);

解释,为什么 React 声明这些类型(来自global.d.ts):

不包含 DOM 库的 React 项目需要这些 接口编译。 React Native 应用程序使用 React,但是有 没有可用的 DOM。 JavaScript 运行时仅适用于 ES6/ES2015。这些 定义允许此类项目仅使用 --lib ES6 进行编译。

警告:所有这些接口都是空的。如果你想输入 各种属性的定义(例如 HTMLInputElement.prototype.value),需要添加--lib DOM(通过 命令行或 tsconfig.json)。

【问题讨论】:

  • you need to add --lib DOM (via command line or tsconfig.json).
  • @Bugbeeb 我确实有(请参阅我发布的沙箱)。
  • 嗯,我怀疑是因为 dom 是一个类型声明文件,所以全局反应类型覆盖了变量声明。如果你尝试写declare var MouseEvent: ...,你会得到一个警告,因为它已经被定义了。有点像在两个不同的文件中写var x = 1var x = 'a',第二个覆盖第一个
  • @Bugbeeb 我也认为这就是正在发生的事情。两者都是全局类型声明文件,但 React 的一个优先且不完整。

标签: reactjs typescript next.js definitelytyped


【解决方案1】:

编辑:正如@Christoph Bühler 指出的那样,addEventListener 事件处理程序似乎收到了MouseEventInit 而不是MouseEvent

所以这行得通:

 const listener: EventListener = (ev: MouseEventInit) => {
   console.log("Click!");

   const e = ev as MouseEvent;
   console.log(e.target);
 };

Working CodeSandbox 是我从你那里分出来的。

【讨论】:

  • 感谢您的回答。正如您在没有 React 的沙箱中看到的那样,MouseEvent 实际上是一个事件:interface MouseEvent extends UIEventinterface UIEvent extends Event 在我看来就像 React 中缺少的类型...
  • 我明白了。我的猜测是 lib.dom.d.ts 会覆盖 TypeScript 中的类型。
  • lib.dom.ts 实际上是正确的,并且来自 TypeScript 本身。 global.d.ts 但是不完整并导致这些问题。我现在正试图以某种方式从tsconfig.json 中排除该文件。不过到目前为止还没有运气。
  • 如果您可以调整答案以使用MouseEventInit 类型而不是Event,我会接受。
  • 好消息@Christoph Bühler。编辑原始答案。附带说明一下,这样做需要将 ev 转换为 MouseEvent 以使用诸如 target 之类的属性。
【解决方案2】:

您可以使用triple-slash directive

我在一个 React + TypeScript 项目中遇到了类似的问题,其中 linter (eslint-typescript) 标记了对 no-unsafe-callno-unsafe-member-access 规则,当使用 DOM API 时。例如,document.getElementById("myId") 会导致这两个错误:

ESLint:any 类型值的不安全调用。 (@typescript-eslint/no-unsafe-call)

ESLint:不安全的成员访问 .getElementById 对 any 值。 (@typescript-eslint/no-unsafe-member-access)

与@Bugbeeb 和@Christoph Bühler 一样,我认为这是因为来自React 的空类型声明以某种方式覆盖了TypeScript 的内部“lib.dom.d.ts”类型定义。

为了解决这个问题,我使用了三斜杠指令(参见 https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-lib-)。在文件的第一行插入///<reference lib="dom"/>,如下所示:

///<reference lib="dom"/>

// Your code
document.getElementById("myId")
// ...

在这一行中,TypeScript 明确地包含了内置的 lib 文件,并且应该优先考虑自己的类型声明而不是 React 的。

注意:在我从事的项目中,我只需在一个文件中执行此操作,这也解决了其他文件中的问题。

【讨论】:

    猜你喜欢
    • 2022-11-11
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 2021-10-17
    • 2020-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多