¿Cómo añado atributos a elementos HTML existentes en TypeScript/JSX?
¿Alguien sabe cómo agregar/extender correctamente todos los atributos de los elementos HTML nativos con atributos personalizados?
Con la documentación de TypeScript sobre la fusión de interfaces, pensé que podría simplemente hacer esto:
interface HTMLElement {
block?: BEM.Block;
element?: BEM.Element;
modifiers?: BEM.Modifiers;
}
<div block="foo" />; // error
Pero obtengo el siguiente error de Intellisense en vscode 1.6.1 (última versión):
[ts] La propiedad 'block' no existe en el tipo 'HTMLProps'.
Las HTMLProps a las que se refieren son React.HTMLProps<T> y el elemento div se declara para usarlo de la siguiente manera:
namespace JSX {
interface IntrinsicElements {
div: React.HTMLProps<HTMLDivElement>
}
}
Intenté redeclarar el div, pero en vano.
Relacionado: https://github.com/Microsoft/TypeScript/issues/11684
Editar: Esto es lo que terminó funcionando para mí:
declare module 'react' {
interface HTMLAttributes<T> extends DOMAttributes<T> {
block?: string
element?: string
modifiers?: Modifiers // <-- custom interface
}
}
Respuesta #1
Parece que en versiones anteriores de los archivos de definición de tipos (v0.14) las interfaces simplemente se declaraban bajo un espacio de nombres global React, por lo que anteriormente se podía usar la sintaxis estándar de combinación.
declare namespace React {
interface HTMLProps<T> extends HTMLAttributes, ClassAttributes<T> {
}
}
Sin embargo, la nueva versión del archivo d.ts (v15.0) ha declarado todo dentro de un módulo. Dado que los módulos no soportan la fusión, hasta donde yo sé, la única opción en este momento parece ser la ampliación de módulos: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
Hice el siguiente experimento y me funcionó:
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>
)
};
Obviamente, esto es bastante tedioso, podrías poner el código de ampliación en otro archivo como se muestra en el ejemplo del manual de TypeScript, e importarlo:
import * as React from 'react';
import './react_augmented';
Pero todavía está bastante sucio. Así que tal vez sea mejor abordar el problema con los colaboradores del archivo de definición de tipo.
Respuesta #2
Quería usar el reemplazo de createElement de glamor, que añade una propiedad css a cada elemento.
Para agregar a la respuesta aceptada, la ampliación de módulos parece funcionar, pero HTMLProps solo funcionó para elementos que no son de entrada. Las interfaces correctas para extender parecen ser HTMLAttributes y SVGAttributes.
declare module 'react' {
interface HTMLAttributes<T> {
css?: any
}
interface SVGAttributes<T> {
css?: any
}
}
Para evitar importar la ampliación del módulo en cada componente, vuelve a exportar createElement:
// createElement.ts
import { createElement } from 'glamor/react'
declare module 'react' {
interface HTMLAttributes<T> {
css?: any
}
interface SVGAttributes<T> {
css?: any
}
}
export default createElement
Luego dile a TS que use nuestro createElement para JSX con este tsconfig:
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "createElement"
}
}
Uso :
// MyComponent.tsx
import createElement from './createElement'
export default function MyComponent() {
return <div css={{ color: 'red' }} />
}
