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
An up-to-date example (May 2019)
React type definition file (by default - index.d.ts
when staring with create-react-app
) contain list of all the standard HTML elements, as well as known attributes.
In order to allow custom HTML attributes, you need to define it's typing.
Do that by expanding HTMLAttributes
interface:
declare module 'react' {
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
// extends React's HTMLAttributes
custom?: string;
}
}
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.
For vue, the following works:
declare module 'vue-tsx-support/types/dom' {
interface InputHTMLAttributes {
autocorrect: string;
autocapitalize
}
}
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' }} />
}