Dynamic props for DOM elements using custom JSX pragma in Typescript

716 views Asked by At

Is it possible to allow DOM element properties to be extended when using a custom JSX pragma?

Similar to how emotion & styled-components use a css prop and manage logic within their jsx pragmas. I would like to do the same, but with properties that can be configured via a generic type?

So, for example:

// @jsx jsx

import { jsx } from 'my-lib';

...

<button<{ primary: boolean }> css={styles} primary={primary}>
  Hello world
</button>

My current type definition looks something like this:

interface CustomAttributes {
    css?: CSSProperties;
}


declare module 'react' {
    interface DOMAttributes extends CustomAttributes {}
}

declare global {
    namespace JSX {
        interface IntrinsicAttributes extends CustomAttributes {}
    }
}

and my naive solution would look something like this:

interface CustomAttributes {
    css?: CSSProperties;
}


declare module 'react' {
    interface DOMAttributes<DynamicProps> extends CustomAttributes, DynamicProps {}
}

declare global {
    namespace JSX {
        interface IntrinsicAttributes<DynamicProps> extends CustomAttributes, DynamicProps {}
    }
}

related question: Styled components's 'css' prop with TypeScript

1

There are 1 answers

0
Linda Paiste On

You can definitely add a fixed set of properties but I can't figure out how to make it dynamic. This works just fine and adds an optional boolean property 'primary' to all JSX elements.

interface CustomAttributes {
    primary?: boolean;
}

declare global {
    namespace JSX {
        interface IntrinsicAttributes extends React.Attributes, CustomAttributes {}
    }
}

When we try to make it generic we run into trouble because the original interface is not generic. Adding a generic <T> causes this error:

TS2428: All declarations of 'IntrinsicAttributes' must have identical type parameters.

There are a bunch of interfaces in the inheritance chain that react uses to determine props (React.DetailedHTMLProps), so there are other possible places that you could add your props.

interface DOMAttributes<T>, interface HTMLAttributes<T>, etc. are generic, but the generic type T refers to the type of the element (ie. button) rather than a generic argument of the element. It is possible to add certain props to all button elements.

The brick wall that I can't get past is how to make the button element itself generic like your example when JSX.IntrinsicElements.button is not generic. I'm not confident enough to say that it's definitely impossible, but I'm not able to do it.