Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using web-components within Preact and typescript

I'm using custom-elements aka web-components within Preact. The problem is that Typescript complains about elements not being defined in JSX.IntrinsicElements - in this case a check-box element:

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

Error message (path omitted):

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

I came across the following, unfortunately not working, possible solutions:

  1. https://stackoverflow.com/a/57449556/7664765 - It's an answer not really realted to the question but it covered my problem

I've tried adding the following to my typings.d.ts file:

import * as Preact from 'preact';

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

My IDE grayed out the import part and IntrinsicElements which means it's not used (?!) and it didn't worked anyway. I'm still getting the same error message.

  1. https://stackoverflow.com/a/55424778/7664765 - Also for react, I've tried to "convert" it to preact and I got the same results as for 1.

I've even found a file maintained by google in the squoosh project where they did the following to "polyfill" the support:

In the same folder as the component a missing-types.d.ts file with the following content, basically the same setup I have but with a index.ts file instead of check-bock.ts and they're using an older TS version v3.5.3:

declare namespace JSX {
  interface IntrinsicElements {
    'range-input': HTMLAttributes;
  }
}

I'm assuming their build didn't fail so how does it work and how do I properly define custom-elements to use them within preact / react components?

I'm currently using [email protected] and [email protected].

like image 620
Simon Avatar asked Apr 03 '20 15:04

Simon


3 Answers

Here are the correct attributes to use, otherwise you will get an error when passing key in for example.

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
    }
  }
}
like image 178
Dominic Avatar answered Oct 09 '22 04:10

Dominic


Okay I managed to solve it using 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;
        }
    }
}

Using the HTMLAttributes interface we can tell JSX which attributes are available for our custom-element:

// 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;
}
like image 6
Simon Avatar answered Oct 09 '22 06:10

Simon


With typescript 4.2.3 and preact 10.5.13, here is what works to define a custom tag name with attributes:

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

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

Differences:

  • The module is 'preact' (must be quoted).
  • The namespace is JSX.
  • The IntrinsicElements value type is an interface that extends HTMLAttributes.
  • It extends HTMLAttributes via the name preact.JSX.HTMLAttributes.
  • It supplies the base element type as HTMLElement to populate the eventTarget type in props/attrs like event listeners. You could also put SVGElement if applicable.
like image 2
Matthias Avatar answered Oct 09 '22 04:10

Matthias