El uso de foo como atributo genera un error:
// App.tsx // 👇 throws const App = () => <div foo></div> export default App Type '{ foo: true; }' is not assignable to type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'. Property 'foo' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.ts(2322) Pero usar foo-foo está bien, ¿por qué?
// App.tsx // 👇 no error is thrown const App = () => <div foo-foo></div> export default AppY lo más importante, ¿cómo definir tipos como este en TypeScript? es decir, solo permitir atributos estándar o de kebab-case.
La respuesta está en la sección JSX del Manual de mecanografiado :
Si un nombre de atributo no es un identificador JS válido (como un atributo
data-*), no se considera un error si no se encuentra en el tipo de atributos del elemento.
Toda la sección sobre JSX es una lectura muy esclarecedora.
Me temo que la respuesta a la pregunta "cómo definir tipos como este en TypeScript" es... No lo hacemos. La compatibilidad con JSX está integrada en el compilador. Sin embargo, hay muchas cosas interesantes que podemos personalizar.
La nota del manual de TS en la respuesta de Fabio explicó todo esto, solo quiero expandirme un poco. En resumen, TS no considera válidos los atributos de kebab-case, pero no generará un error; pero los atributos precedidos por data- o aria- se consideran válidos.
React ( desde 16 ) acepta atributos personalizados, es decir, <div foo /> y <div whateverYouLike={2}> deberían funcionar.
Lo que encuentro confuso con React es que data-* y aria-* deben escribirse tal cual, en lugar de convertirlos a camelCase como todo lo demás. Especialmente cuando estos atributos se convierten en camelCase en DOM vainilla:
<div data-my-age="100" aria-label="A Test" /> const $div = document.querySelector('#test') $div.dataset.myName = "D" console.log({ dataset: $div.dataset }) // { myAge: "100", myName: "D" } console.log($div.ariaLabel) // "A Test"Nunca se dan razones para esto, por lo que solo podemos especular. Tal vez algo que ver con todas las herramientas, conveniencia de análisis, etc.
Las razones por las que <div foo /> arroja en TS es porque TS proporciona un conjunto estricto de nombres de propiedad válidos. Sin embargo, como se indica en la otra respuesta, TS no arrojará un error en random-foo porque se considera un identificador JS no válido. Mi especulación se debe a que los elementos DOM permiten propiedades arbitrarias, por lo que este es un compromiso que permite escribir correctamente en la mayoría de los casos en TS pero proporciona algún tipo de vía de escape. Me encantaría saber las razones detrás de estas decisiones.
¿Cómo definir tipos como este en TypeScript? es decir, solo permitir atributos estándar o de kebab-case.
Como ya ha señalado Fabio, el compilador incluye compatibilidad con JSX. Sin embargo, además de la capacidad de identificar lo que constituye un nombre de atributo válido, no creo que haya mucha magia: hay una lista completa de atributos DOM válidos. TS no arroja un error si mezcla casos de kebab y camello, es decir, <div data-myName> funciona, <div myName/> no, etc., por lo que tampoco difiere por el uso de mayúsculas y minúsculas.
Si conoce todos sus accesorios válidos de antemano, puede emular lo mismo.
// allow only these prop names, which happened to be all camelCased interface MyThing { name: string myName: string anotherProp: string }En el caso de kebab-case, los tipos de literales de plantilla podrían ser útiles:
type ValidPrefix = "data" | "aria"; type ValidSuffix = "banana" | "apple" | "pear"; type ComputedProps = { [key in `${ValidPrefix}-${ValidSuffix}`]?: string; }; const x: ComputedProps = { "data-apple": 'hi' };Más allá de esto, actualmente no hay ningún mecanismo en TS que pueda diferir entre camelCase y kebab-case string.
Si está buscando una forma de aumentar JSX para permitir accesorios personalizados y elementos personalizados, esta es una forma de hacerlo:
Aumento del atributo JSX para permitir accesorios personalizados y elementos personalizados