SyMind / learning

路漫漫其修远兮,吾将上下而求索。
9 stars 1 forks source link

在使用 TypeScript 的 React 项目中使用 Web Component #30

Open SyMind opened 2 years ago

SyMind commented 2 years ago

原文:https://goulet.dev/posts/consuming-web-component-react-typescript/

在我构建了第一个 Web Component 后,想在使用 TypeScript 的 React 项目中使用它。当我在 React 中添加了它后,得到了一个错误:[ts] Property 'wc-menu-button' does not exist on type 'JSX.IntrinsicElements'. [2339]

image

导致这个问题的原因是,React 仅知道标准的 HTML 元素,所以当我添加自定义的元素后,React 无法识别它,并向我展示一个错误。

有两种方案解决这个问题。一种在 Web Component 项目中处理,另一种在 React 项目中处理。

方案一:在 Web Component 中定义类型

如果你是 Web Component 的作者,你可以在 JSX 的 IntrinsicElements 中定义你的组件,如下:

declare global {
  namespace JSX {
    interface IntrinsicElements {
      "my-element": any;
    }
  }
}

你可能会觉的将 my-element 的类型设为 any 并不理想,你是对的。是不是将组件的属性定义为一个接口,让后将组件设置为该类型会更好?像下面这样:

declare global {
  namespace JSX {
    interface IntrinsicElements {
      "my-element": MyElementAttributes;
    }

    interface MyElementAttributes {
      name: string;
    }
  }
}

这看起来很好。但当你向这个自定义组件设置 keyref 属性时,将看到这样的错误 [ts] Property 'ref' does not exist on type 'MyElementAttributes'. [2339]

image

MyElementAttributes 需要继承 React 的 HTMLAttributes 类,如下:

interface MyElementAttributes extends HTMLAttributes {
  name: string;
}

现在当你尝试使用 tsc(TypeScript 编译器)编译你的 Web Component 时将报错,因为编译器无法获取 HTMLAttributes 类型。你需要安装 @types/react 作为开发时的依赖。但是现在你的 Web Component 项目依赖于 React 类型,这是一种倒退,你基于标准自定义的组件不应该依赖于某个前端框架。你可以依赖 React,也可以将你的组件声明为 any 类型,然后编写完备的文档去描述所有属性。

或使用一些工具来帮忙

如果你不想自己处理生成 IntrinsicElement 类型的问题,你可以使用 StencilJS 类似的工具来构建你的 Web Component。它将为你处理类型,生成一个全局声明将你的 Web Component 定义在 IntrinsicElements 接口中。

方案二:在 React 项目中定义类型

与方案一类似。该方案将类型定义在 React 项目中,而不是 Web Component 项目中。我习惯创建一个 declarations.d.ts 文件,然后将类型定义在这里:

declare namespace JSX {
  interface IntrinsicElements {
    "wc-menu-button": any;
  }
}

现在你可以在 React 项目中使用你的 Web Component 了。