¿Cómo tipas los eventos en React con TypeScript? Por ejemplo, el `onChange` de un input o el `onSubmit` de un formulario.
Ver respuesta — intenta responderla en voz alta primero
React trae sus propios tipos de evento genéricos: ChangeEvent<HTMLInputElement>, FormEvent<HTMLFormElement>, MouseEvent<HTMLButtonElement>, etc. El genérico indica sobre qué elemento HTML ocurre el evento.
React no usa los eventos nativos del navegador directamente: los envuelve en "synthetic events" para que funcionen igual en todos los navegadores. Por eso los tipos no salen del DOM, sino del paquete de React: los importas desde "react".
- El patrón es
TipoDeEvento<ElementoHTML>: el primer nombre dice qué pasó (change, submit, click) y el genérico dice sobre qué elemento. - Los más comunes:
ChangeEvent<HTMLInputElement>para inputs,FormEvent<HTMLFormElement>para el submit de un formulario yMouseEvent<HTMLButtonElement>para clics en botones. - Truco para no memorizarlos: escribe el handler inline (
onChange={(e) => ...}) y TypeScript infiere el tipo solo. Luego, si quieres extraer la función, pasa el cursor sobreey copia el tipo que te muestra el editor. e.currentTargetes el elemento donde pusiste el handler y siempre está bien tipado según el genérico.e.targetes donde se originó el evento (puede ser un hijo), así que su tipo es más genérico. En un input cononChangepuedes usare.target.valuesin problema; en contenedores, prefierecurrentTarget.
import { useState } from "react";
import type { ChangeEvent, FormEvent, MouseEvent } from "react";
function FormularioContacto() {
const [correo, setCorreo] = useState("");
function manejarCambio(e: ChangeEvent<HTMLInputElement>) {
setCorreo(e.target.value); // value es string, bien tipado
}
function manejarEnvio(e: FormEvent<HTMLFormElement>) {
e.preventDefault(); // evita recargar la página
console.log("Enviando:", correo);
}
function manejarLimpiar(e: MouseEvent<HTMLButtonElement>) {
console.log("Botón:", e.currentTarget.name); // currentTarget bien tipado
setCorreo("");
}
return (
<form onSubmit={manejarEnvio}>
<input type="email" value={correo} onChange={manejarCambio} />
{/* Truco: si el handler va inline, TypeScript infiere el tipo de e solito */}
<button type="button" name="limpiar" onClick={manejarLimpiar}>Limpiar</button>
<button type="submit">Enviar</button>
</form>
);
}
Tipar el evento con el tipo nativo del navegador o con any:
// El tipo nativo del DOM no encaja con el synthetic event de React
function manejarCambio(e: Event) {
setCorreo(e.target.value);
// Error: Property 'value' does not exist on type 'EventTarget'.
}
La corrección es usar el tipo de React con el elemento correcto:
import type { ChangeEvent } from "react";
function manejarCambio(e: ChangeEvent<HTMLInputElement>) {
setCorreo(e.target.value); // ahora TypeScript sabe que value existe
}
"React envuelve los eventos nativos en synthetic events, así que uso los tipos que exporta React:
ChangeEvent<HTMLInputElement>para inputs,FormEvent<HTMLFormElement>para formularios yMouseEvent<HTMLButtonElement>para clics. El genérico indica el elemento donde va el handler. Si no recuerdo el nombre exacto, escribo el handler inline y TypeScript lo infiere; luego copio ese tipo si quiero extraer la función. También cuido la diferencia entretarget, que es donde se originó el evento, ycurrentTarget, que es el elemento con el handler y siempre queda bien tipado."
Crea un componente Buscador con un input controlado y un formulario. Al enviar, debe hacer preventDefault e imprimir el texto buscado. Tipa los dos handlers como funciones separadas (no inline).
Ver solución
import { useState } from "react";
import type { ChangeEvent, FormEvent } from "react";
function Buscador() {
const [texto, setTexto] = useState("");
function manejarCambio(e: ChangeEvent<HTMLInputElement>) {
setTexto(e.target.value);
}
function manejarEnvio(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
console.log("Buscando:", texto);
}
return (
<form onSubmit={manejarEnvio}>
<input value={texto} onChange={manejarCambio} placeholder="Buscar..." />
<button type="submit">Buscar</button>
</form>
);
}