Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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     } } 
like image 363
jedmao Avatar asked Oct 17 '16 18:10

jedmao


People also ask

How do you add attribute to HTML element in React?

To add custom HTML attributes in React, we can just add them as we do with regular HTML element attributes. We just add the custom-attribute custom attribute and it'll be rendered in the HTML. This works since React 16.

Can I add my own attributes to HTML elements?

If you want to define your own custom attributes in HTML, you can implement them through data-* format. * can be replaced by any of your names to specify specific data to an element and target it in CSS, JavaScript, or jQuery.

How do I add a custom attribute to an element?

So how do we add custom attributes to HTML elements? We can create any custom attribute, by adding, data-, followed by the attribute name.


2 Answers

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.

like image 156
Edwin Avatar answered Sep 23 '22 13:09

Edwin


I wanted to use glamor's createElement replacement which adds a css prop to every element.

To add to the accepted answer, module augmentation seems to do the trick but HTMLProps only worked for non-input elements. The correct interfaces to extend seems to be HTMLAttributes and SVGAttributes.

declare module 'react' {   interface HTMLAttributes<T> {     css?: any   }    interface SVGAttributes<T> {     css?: any   } } 

To avoid importing the module augmentation in every component, re-export createElement:

// createElement.ts import { createElement } from 'glamor/react'  declare module 'react' {   interface HTMLAttributes<T> {     css?: any   }    interface SVGAttributes<T> {     css?: any   } }  export default createElement 

Then tell TS to use our createElement for JSX with this tsconfig:

{   "compilerOptions": {     "jsx": "react",     "jsxFactory": "createElement"   } } 

Usage:

// MyComponent.tsx import createElement from './createElement'  export default function MyComponent() {   return <div css={{ color: 'red' }} /> } 
like image 22
jschr Avatar answered Sep 23 '22 13:09

jschr