typescriptjsx

How do I add attributes to existing HTML elements in TypeScript/JSX?


Anyone know how to properly add/extend all native HTML element attributes with custom ones?

With the TypeScript documentation for merging interfaces, I thought that I could just do this:

interface HTMLElement {
    block?: BEM.Block;
    element?: BEM.Element;
    modifiers?: BEM.Modifiers;
}

<div block="foo" />; // error

But I get the following Intellisense error in vscode 1.6.1 (latest):

[ts] Property 'block' does not exist on type 'HTMLProps'.

The HTMLProps to which they are referring is React.HTMLProps<T> and the div element is declared to use it like so:

namespace JSX {
    interface IntrinsicElements {
        div: React.HTMLProps<HTMLDivElement>
    }
}

I tried redeclaring the div, but to no avail.

Related: https://github.com/Microsoft/TypeScript/issues/11684

Edit: Here's what ended up working for me:

declare module 'react' {
    interface HTMLAttributes<T> extends DOMAttributes<T> {
        block?: string
        element?: string
        modifiers?: Modifiers // <-- custom interface
    }
}

Solution

  • Looks like in older versions of type definition files (v0.14) the interfaces were simply declared under a global React namespace, so previously you could use the standard merging syntax.

    declare namespace React {
    
        interface HTMLProps<T> extends HTMLAttributes, ClassAttributes<T> {
        }
    }
    

    However the new version of d.ts file (v15.0) have declared everything inside a module. Since modules do not support merging, to the best of my knowledge the only option right now seems to be module augmentation: https://www.typescriptlang.org/docs/handbook/declaration-merging.html

    I did the following experiment and it worked for me:

    import * as React from 'react';
    
    declare module 'react' {
         interface HTMLProps<T> {
            block?:string;
            element?:string;
            modifiers?:string;
        }
    
    }
    
    export const Foo = () => {
    
        return (
            <div block="123" element="456">
            </div>
        )
    };
    

    Obviously this is quite tedious, you could put the augmentation code in another file as shown in the example from the typescript handbook, and import it:

    import * as React from 'react';
    import './react_augmented';
    

    But it's still quite dirty. So maybe it's best to address the issue with the contributors of the type definition file.