如何使用TypeScript和React的Web组件(附代码)

291 阅读2分钟

如何使用TypeScript和React的Web组件

上一篇文章中,我们学习了如何在React中使用Web组件。React最近增加了对Web Components的实验性支持,使我们的React应用能够利用庞大的Web Components生态系统。我们也可以在TSX/TypeScript模板中使用Web Components,为我们的组件提供类型检查。

在我们的例子中,我们将使用前一个教程中的相同的警报Web组件:

A Web Component working in React

为了在我们的React组件中使用警报,我们导入该组件并添加x-alert 标签:

import React, { useState }  from 'react';
import './alert.js';

export default function App() {
  const [show, setShow] = useState(true);

  return (
    <div>
      <button onClick={() => setShow(!show)}>toggle alert</button>

      <x-alert hidden={show} status="success" closable oncloseChange={() => setShow(!show)}>
        This is a Web Component in React
      </x-alert>
    </div>
  );
}

随着最近对自定义元素的支持,我们现在可以在我们的Web组件元素上设置属性和监听事件。然而,如果我们使用TypeScript,我们可能会在我们的TSX文件中看到一些错误,就像下面这样。

Unknown Web Component in TSX file

在运行时,React将与我们的Web组件正确工作。然而,TypeScript不能对我们的TSX模板进行类型检查,因为它不知道如何找到我们相关标签的CustomElement类引用。我们可以将我们的标签添加到JSX.IntrinsicElements 全局接口,这将把x-alertXAlert 类引用联系起来:

import React, { useState, DOMAttributes }  from 'react';
import { XAlert } from './alert.js';
import './alert.js';

type CustomElement<T> = Partial<T & DOMAttributes<T> & { children: any }>;

declare global {
  namespace JSX {
    interface IntrinsicElements {
      ['x-alert']: CustomElement<XAlert>;
    }
  }
}

使用TypeScript泛型,我们可以将我们的Custom Element类XClass ,并结合到ReactDOMAttributes 。这将添加React事件类型,如onClick 到我们的元素。现在,如果我们看一下我们的元素,我们应该看到下面的情况:

Web Component CustomEvents in TSX and React

我们的组件不能识别我们的Web组件在用户点击关闭按钮时发出的closeChange 事件。然而,我们可以利用TypeScript模板字面类型来轻松地定义哪些事件是可用的。

type CustomEvents<K extends string> = { [key in K] : (event: CustomEvent) => void };

type CustomElement<T, K extends string> = Partial<T & DOMAttributes<T> & { children: any } & CustomEvents<`on${K}`>>;

declare global {
  namespace JSX {
    interface IntrinsicElements {
      ['x-alert']: CustomElement<XAlert, 'closeChange' | 'openChange'>;
    }
  }
}

使用模板字面类型,我们可以创建一个字符串类型的列表,并生成一个新的字符串类型列表,其前缀为React所需的on 。然后我们可以把我们的事件字符串传递给CustomEvents 类型,它将为我们添加函数签名。所以现在我们在React和TSX的Web组件上对我们的自定义事件进行了类型检查。

import React, { useState, DOMAttributes }  from 'react';
import { XAlert } from './alert.js';
import './alert.js';

type CustomEvents<K extends string> = { [key in K] : (event: CustomEvent) => void };

type CustomElement<T, K extends string> = Partial<T & DOMAttributes<T> & { children: any } & CustomEvents<`on${K}`>>;

declare global {
  namespace JSX {
    interface IntrinsicElements {
      ['x-alert']: CustomElement<XAlert, 'closeChange' | 'openChange'>;
    }
  }
}

export default function App() {
  const [show, setShow] = useState(true);

  return (
    <div>
      <button onClick={() => setShow(!show)}>toggle alert</button>

      <x-alert hidden={show} status={'success'} closable oncloseChange={() => setShow(!show)}>
        This is a Web Component in React
      </x-alert>
    </div>
  );
}

有了一点TypeScript泛型和模板字面类型的魔力,我们可以使用TypeScript、React和Web组件创造一个优秀的开发者体验。