在 Preact 和 typescript 中使用 web 组件

Posted

技术标签:

【中文标题】在 Preact 和 typescript 中使用 web 组件【英文标题】:Using web-components within Preact and typescript 【发布时间】:2020-07-15 19:59:37 【问题描述】:

我在Preact 中使用custom-elements 又名网络组件。问题是 Typescript 抱怨没有在 JSX.IntrinsicElements 中定义元素 - 在这种情况下是 check-box 元素:

<div className=styles.option>
    <check-box checked=this.prefersDarkTheme ref=this.svgOptions.darkTheme/>
    <p>Dark theme</p>
</div>

错误信息(路径省略):

ERROR in MyComponent.tsx
[tsl] ERROR in MyComponent.tsx(50,29)
      TS2339: Property 'check-box' does not exist on type 'JSX.IntrinsicElements'.

我遇到了以下可能的解决方案,不幸的是没有工作:

    https://***.com/a/57449556/7664765 - 这是一个与问题无关的答案,但它涵盖了我的问题

我已尝试将以下内容添加到我的 typings.d.ts 文件中:

import * as Preact from 'preact';

declare global 
    namespace JSX 
        interface IntrinsicElements 
            'check-box': any; // The 'any' just for testing purposes
        
    

我的 IDE 将导入部分和 IntrinsicElements 显示为灰色,这意味着它没有被使用(?!)并且它无论如何都没有工作。我仍然收到相同的错误消息。

    https://***.com/a/55424778/7664765 - 同样对于反应,我尝试将其“转换”为 preact,我得到了与 1 相同的结果。

我什至在 squoosh 项目中找到了由 google 维护的 file,他们在其中执行以下操作来“填充”支持:

在与组件相同的文件夹中,包含以下内容的 missing-types.d.ts 文件,基本上与我的设置相同,但使用 index.ts 文件而不是 check-bock.ts,并且他们使用的是较旧的 TS 版本 v3.5.3

declare namespace JSX 
  interface IntrinsicElements 
    'range-input': htmlAttributes;
  

我假设他们的构建没有失败,那么它是如何工作的以及如何正确定义自定义元素以在 preact / react 组件中使用它们?

我目前正在使用typescript@v3.8.3preact@10.3.4

【问题讨论】:

【参考方案1】:

这里是正确使用的属性,否则在传递key 时会报错。

declare global 
  namespace JSX 
    interface IntrinsicElements 
      'xx-element1': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>; // Normal web component
      'xx-element2': React.DetailedHTMLProps<React.HTMLAttributes<HTMLInputElement>, HTMLInputElement>; // Web component extended from input
    
  

【讨论】:

【参考方案2】:

好的,我设法使用module augmentation 解决了它:

declare module 'preact/src/jsx' 
    namespace JSXInternal 

        // We're extending the IntrinsicElements interface which holds a kv-list of
        // available html-tags.
        interface IntrinsicElements 
            'check-box': unknown;
        
    

使用HTMLAttributes 接口,我们可以告诉 JSX 哪些属性可用于我们的自定义元素:

// Your .ts file, e.g. index.ts
declare module 'preact/src/jsx' 
    namespace JSXInternal 
        import HTMLAttributes = JSXInternal.HTMLAttributes;

        interface IntrinsicElements 
            'check-box': HTMLAttributes<CheckBoxElement>;
        
    


// This interface describes our custom element, holding all its
// available attributes. This should be placed within a .d.ts file.
declare interface CheckBoxElement extends HTMLElement 
    checked: boolean;

【讨论】:

这是未来的自己。 哇,是的,我又来了。希望我能多次支持这个答案。【参考方案3】:

使用 typescript 4.2.3 和 preact 10.5.13,以下是使用属性定义自定义标签名称的方法:

declare module 'preact' 
    namespace JSX 
        interface IntrinsicElements 
            'overlay-trigger': OverlayTriggerAttributes;
        
    


interface OverlayTriggerAttributes extends preact.JSX.HTMLAttributes<HTMLElement> 
    placement?: string;

区别:

模块为'preact'(必须加引号)。 命名空间是JSXIntrinsicElements 值类型是扩展 HTMLAttributes 的接口。 它通过名称 preact.JSX.HTMLAttributes 扩展 HTMLAttributes。 它将基本元素类型提供为HTMLElement,以像事件侦听器一样填充道具/属性中的 eventTarget 类型。如果适用,您也可以输入SVGElement

【讨论】:

这适用于我使用 TypeScript 4.3.5 和 Preact 10.5.15,但需要注意的是,我还需要在声明之前import 'preact'【参考方案4】:

有一种更好的方法可以做到这一点,而无需手动绑定事件。

您可以使用 @lit-labs/reactcreateComponent 将 web 组件包装到 React 组件。

import * as React from "react";

import  createComponent  from "@lit-labs/react";
import Slrange from "@shoelace-style/shoelace/dist/components/range/range.js";

export const Range = createComponent(React, "sl-range", Slrange,
    change: "sl-change" // map web component event to react event
);

import  Range  from "./SlRange";

export default function App() 
  return (
    <div className="App">
      <Range max=6 min=2 step=2></Range>
    </div>
  );


【讨论】:

它仍然是一个 Web 组件,只是被抽象掉了

以上是关于在 Preact 和 typescript 中使用 web 组件的主要内容,如果未能解决你的问题,请参考以下文章

使用 Preact + Typescript 的类型安全事件处理程序

最新的 preact 和 typescript 模块传递了错误的类型

preact-cli/babel/typescript - 让 Symbol.iterator 和 [...spread] 正常工作

如何使用 preact-router 以编程方式导航?

在 Preact 中渲染原始 HTML 而不使用 Preact 标记?

如何创建具有 HTMLAttributes 作为道具的组件