javascriptreactjstypescriptweb-componentnative-web-component

Typescript error "Property does not exist on type 'JSX.IntrinsicElements'" when using native web component


I'm working with a project that uses React and Typescript, but I want to start using native web components in my project to phase out some of my React components.

I'm getting this error when I try to include use a person-info component in some of my JSX.

Property does not exist on type 'JSX.IntrinsicElements'

I've looked at some of the other questions that have had these issues, but none of them seem to have anything to do with native web components in particular.

How do I get Typescript and React to play nicely when I use my web components in my project?

PersonInfo.mjs

const css = `
  <style>
    :host([hidden]) { display: none; }
    :host {
      align-items: center;
      display: grid;
      font-weight: normal;
      grid-gap: var(--spacing-size-a) var(--spacing-size-a);
      grid-template-areas:
        'picture heading'
        'picture sub-heading';
      grid-template-columns: auto 1fr;
      justify-items: start;
    }
    div {
      grid-area: picture;
    }
    h1, h2 {
      margin: 0;
      padding: 0;
    }
    h1 {
      align-self: end;
      font-size: var(--l-text-size);
      font-weight: normal;
      grid-area: heading;
      text-transform: capitalize;
    }
    h2 {
      align-self: start;
      font-size: var(--m-text-size);
      grid-area: sub-heading;
    }
    ion-icon {
      font-size: 56px;
    }
  </style>
`

const html = `
  <div>
    <ion-icon name="md-contact"></ion-icon>
  </div>
  <h1>Heading</h1>
  <h2>Sub-heading</h2>
`

class PersonInfo extends HTMLElement {
  static get observedAttributes () {
    return [
      'heading',
      'subHeading',
      'size'
    ]
  }

  constructor () {
    super()

    this.attachShadow({mode: 'open'})
    this.shadowRoot.appendChild(template.content.cloneNode(true))
  }

  connectedCallback () {
    this.shadowRoot.querySelector('h1').innerText = this.getAttribute('heading')
    this.shadowRoot.querySelector('h2').innerText = this.getAttribute('subHeading')
  }

  get heading () {
    return this.getAttribute('heading')
  }
  set heading (newValue) {
    this.setAttribute('heading', newValue)
    this.shadowRoot.querySelector('h1').innerText = newValue
  }

  get subHeading () {
    return this.getAttribute('subHeading')
  }
  set subHeading (newValue) {
    this.setAttribute('subHeading', newValue)
    this.shadowRoot.querySelector('h2').innerText = newValue
  }

  get size () {
    return this.getAttribute('size')
  }
  set size (newValue) {
    this.setAttribute('size', newValue)
  }
}

const template = document.createElement('template')
template.innerHTML = `${css}${html}`

window.customElements.define('person-info', PersonInfo)

Import statement

import '../../common/WebComponents/PersonInfo.mjs'

Usage in JSX

<main>
  <person-info
    heading='Bruce Wayne'
    subHeading="I'M BATMAN!"
  />
</main>

Solution

  • I figured out after going here how to get this particular error to go away.

    import * as React from 'react'
    
    declare global {
        namespace JSX {
            interface IntrinsicElements {
                'person-info': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
            }
        }
    }
    

    I got another error after that, however, due to the custom attributes I use on my component. Thanks to Shanon's comment, I figured out how to fix that too and ended up with this final code that I just imported in my App.tsx file.

    import * as React from 'react'
    
    declare global {
      namespace JSX {
        interface IntrinsicElements {
          'person-info': PersonInfoProps
        }
      }
    }
    
    interface PersonInfoProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> {
      heading: string,
      subHeading: string,
      size?: string
    }