solidjs / solid-docs-next

SolidJS Docs.
https://docs.solidjs.com/
212 stars 244 forks source link

[Request]: Add Web Components Typing to the TypeScript guide #823

Open ryansolid opened 1 month ago

ryansolid commented 1 month ago

What is this request related to?

Request

📋 Suggested

https://docs.solidjs.com/configuration/typescript#typescript

📋 General description or bullet points

It's come to my attention that we don't talk about how to use webcomponents in the new docs anywhere. There was an old discussion here: https://github.com/solidjs/solid/issues/616 that sheds some light but the answer is little bit lost.

It doesn't necessarily need to go in the TS section but it does feel like it makes sense because it uses similar overrides to custom events, props etc..

🖥️ Reproduction of code samples in StackBlitz

No response

trusktr commented 2 hours ago

Here's what I do for Lume elements for example:

https://github.com/lume/lume/blob/b5242eb39d1f3613c1071f6db40d954ca559faf6/src/lights/PointLight.ts#L163-L175

declare module 'solid-js' {
    namespace JSX {
        interface IntrinsicElements {
            'lume-point-light': ElementAttributes<PointLight, PointLightAttributes>
        }
    }
}

declare global {
    interface HTMLElementTagNameMap {
        'lume-point-light': PointLight
    }
}

That declare module 'solid-js' {...} adds the element type only for Solid JSX. As these elements are written with Solid, I chose for them to have out-of-the-box JSX types for Solid.

If you want to also support other frameworks like React, Preact, Vue, Svelte, etc (they all use JSX type defs) then you can make separate files for each framework (because they may have different JSX type layouts) that someone can import to gain the types. For example here's the React JSX definition for that same lume-point-light element, which someone can import in a React app to gain those types in their React JSX templates:

https://github.com/lume/lume/blob/b5242eb39d1f3613c1071f6db40d954ca559faf6/src/lights/PointLight.react-jsx.d.ts

import type {ReactElementAttributes} from '@lume/element/src/react'

// React users can import this to have appropriate types for the element in their JSX markup.
declare global {
    namespace JSX {
        interface IntrinsicElements {
            'lume-point-light': ReactElementAttributes<PointLight, PointLightAttributes>
        }
    }
}

ElementAttributes and ReactElementAttributes are helpers for my way of defining elements with @lume/element, but the gist is that they pluck properties off of the element class type for use in JSX (for example, you don't want everything including for example addEventListener or appendChild to appear as a prop in your JSX template. So I create a list of which props to pluck, f.e.:

export type PointLightAttributes = LightWithShadowAttributes | 'distance' | 'decay'

where LightWithShadowAttributes is the list of properties inherited from the base class.