stencil icon indicating copy to clipboard operation
stencil copied to clipboard

bug: Stencil Generated components.d.ts Missing Kebab-Case Property Typings in HTML Element Interfaces

Open BenjaminBrossi opened this issue 10 months ago • 4 comments

Prerequisites

Stencil Version

4.27.2

Current Behavior

Bug Ticket

Description

The Stencil compiler generates a components.d.ts file that defines component interfaces using camelCase property names in the Components namespace, but fails to include kebab-case alternatives in the HTML element interfaces. This causes type errors when using the web components in React or other frameworks that use kebab-case attribute names in JSX.

Current Behavior

Currently, the generated components.d.ts file:

  1. Defines component properties in camelCase within the Components namespace:

    export namespace Components {
      interface MdxProductCard {
        "availability"?: "available" | "unavailable" | "disabled";
        "brand"?: string;
        "discountPrice"?: number;
        // other properties...
      }
    }
    
  2. HTML element interfaces extend the Components interfaces:

    interface HTMLMdxProductCardElement extends Components.MdxProductCard, HTMLStencilElement {
      // event listeners...
    }
    
  3. The React and Stencil JSX typings need to be manually added with a custom script to support kebab-case attributes:

    declare module "react" {
      export namespace JSX {
        interface IntrinsicElements {
          "mdx-product-card": {
            "discount-price"?: number | string;
            "discountPrice"?: number | string;
            // other properties...
          };
        }
      }
    }
    

Expected Behavior

Expected Behavior

The Stencil compiler should automatically generate HTML element interfaces that support both camelCase and kebab-case property names:

  1. The Components namespace should continue to use camelCase for TypeScript usage.

  2. The HTML element interfaces should support both camelCase and kebab-case attributes:

    interface HTMLMdxProductCardElement extends Components.MdxProductCard, HTMLStencilElement {
      // Add support for kebab-case attributes
      "discount-price"?: number;
      // existing camelCase properties from Components.MdxProductCard
      // event listeners...
    }
    
  3. The React and Stencil JSX typings should be automatically generated with both camelCase and kebab-case attributes.

System Info


Steps to Reproduce

Steps to Reproduce

  1. Create a Stencil component with camelCase properties:

    @Component({ tag: "my-component" })
    export class MyComponent {
      @Prop() discountPrice: number;
    }
    
  2. Build the component library with stencil build

  3. Examine the generated components.d.ts file

  4. Try to use the component in React with kebab-case attributes:

    <my-component discount-price={5} />
    
  5. Observe TypeScript errors because the kebab-case attribute is not defined in the typings.

Code Reproduction URL

https://github.com/BenjaminBrossi/stencil-typings

Additional Information

Impact

This issue forces teams to create and maintain custom scripts to generate proper typings for React and other frameworks. Without these scripts, developers experience type errors when using kebab-case attributes in JSX, even though the web components themselves accept these attributes at runtime.

Environment

  • Stencil version: 4.27.2
  • TypeScript version: 5.7.3
  • React version: 19

Workaround

We currently use a custom script to generate proper typings for React and Stencil JSX. This script:

  1. Extracts all component properties from the generated components.d.ts
  2. Converts camelCase properties to kebab-case
  3. Generates React and Stencil JSX module declarations with both camelCase and kebab-case properties
  4. Appends these declarations to the components.d.ts file

This workaround is maintenance-intensive and should be unnecessary if Stencil properly supported kebab-case attributes in its generated typings.

BenjaminBrossi avatar Mar 12 '25 08:03 BenjaminBrossi

As mentioned in the docs:

When we use our component in a TSX file, an attribute uses camelCase

as well as:

In HTML, the attribute must use 'dash-case'

I would actually consider it a bug that kebab case works in JSX, Stencil was never suppose to support it. Can you please elaborate why you prefer kebab casing in your use case? Are your users using raw Stencil components in React applications? Are you suggesting that requiring camelCase in JSX and kebab case in HTML is confusing?

christian-bromann avatar Mar 12 '25 17:03 christian-bromann

The Problem is, that we have customers, that want that typing in JSX-Files, without using the react wrapped components.

BenjaminBrossi avatar Mar 18 '25 08:03 BenjaminBrossi

The Problem is, that we have customers, that want that typing in JSX-Files, without using the react wrapped components.

Maybe, as a recommendation, you could consider providing custom manifest elements for JSX typings. The links below can help you out 🙂 :

  • https://custom-elements-manifest.open-wc.org/analyzer/getting-started/
  • https://github.com/break-stuff/cem-tools/tree/main/packages/jsx-integration#readme

dgonzalezr avatar Mar 18 '25 11:03 dgonzalezr

The Problem is, that we have customers, that want that typing in JSX-Files, without using the react wrapped components.

@BenjaminBrossi You don't need React wrapped components. If your customers use JSX they should use camel cased props, if they write in raw HTML files they must use kebab casing. If they need type support in HTML files they our web-types community project may be useful to them.

christian-bromann avatar Mar 18 '25 13:03 christian-bromann