
Los eventos de React son objetos sintéticos que envuelven los eventos nativos del DOM y añaden compatibilidad entre navegadores. TypeScript expone el tipado completo de esos eventos en el namespace React, de modo que cada manejador recibe exactamente el tipo que describe su origen (button, input, form). Dominar esos tipos es esencial para escribir formularios sólidos, controles accesibles y páginas que no fallen cuando el usuario interactúa de formas inesperadas.
Tipos principales de eventos sintéticos
Los tres tipos de evento que aparecen en casi todos los formularios son ChangeEvent, MouseEvent y FormEvent. Todos son genéricos y reciben como parámetro el elemento HTML que disparó el evento. Ese genérico es el que permite acceder a propiedades tipadas como event.target.value sin hacer casting.
import type { ChangeEvent, MouseEvent, FormEvent } from "react";
function cambioTexto(event: ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
function clickBoton(event: MouseEvent<HTMLButtonElement>) {
console.log("Botón pulsado", event.currentTarget.textContent);
}
function enviarFormulario(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const datos = new FormData(event.currentTarget);
console.log(Object.fromEntries(datos));
}
Los nombres siguen un patrón muy consistente. Si necesitas un evento de teclado es KeyboardEvent<HTMLInputElement>; si necesitas un evento de foco es FocusEvent<HTMLInputElement>. La documentación de los tipos de React en @types/react contiene la lista completa, pero en la práctica los tres anteriores cubren la mayor parte de los manejadores de un formulario.
Cuando tipas
event.currentTarget, obtienes el elemento al que está atado el manejador. Cuando usasevent.target, obtienes el elemento que disparó el evento, que puede ser distinto si se delega a un contenedor. UsacurrentTargetpara formularios y controles, ytargetpara delegación de eventos.
Formularios controlados con estado tipado
Un formulario controlado mantiene el valor de cada campo en el estado del componente. En TypeScript lo habitual es declarar una interfaz con la forma del formulario y pasarla al useState como genérico.
import { useState, type ChangeEvent, type FormEvent } from "react";
interface RegistroForm {
nombre: string;
email: string;
edad: number;
}
const VALOR_INICIAL: RegistroForm = {
nombre: "",
email: "",
edad: 0,
};
export function Registro() {
const [form, setForm] = useState<RegistroForm>(VALOR_INICIAL);
function handleChange(event: ChangeEvent<HTMLInputElement>) {
const { name, value } = event.target;
setForm((prev) => ({
...prev,
[name]: event.target.type === "number" ? Number(value) : value,
}));
}
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
console.log("Enviado", form);
}
return (
<form onSubmit={handleSubmit}>
<input name="nombre" value={form.nombre} onChange={handleChange} />
<input name="email" type="email" value={form.email} onChange={handleChange} />
<input name="edad" type="number" value={form.edad} onChange={handleChange} />
<button type="submit">Registrar</button>
</form>
);
}
La función handleChange es genérica para todos los inputs del formulario gracias a la propiedad name. El tipo ChangeEvent<HTMLInputElement> garantiza que event.target.value siempre sea un string y que podamos distinguir el tipo del control con event.target.type para aplicar la conversión adecuada.
Varios controles y tipos por campo
Cuando el formulario incluye select, textarea o input de tipos distintos, declara un manejador por cada familia de elementos. Cada uno recibe el tipo de evento correcto.
import type { ChangeEvent } from "react";
function cambioTexto(event: ChangeEvent<HTMLInputElement>) {}
function cambioArea(event: ChangeEvent<HTMLTextAreaElement>) {}
function cambioSelect(event: ChangeEvent<HTMLSelectElement>) {}
Esta separación evita casteos manuales y mantiene el autocompletado específico de cada elemento, como selectedIndex en el select o rows en el textarea.
Validación antes del envío
El método event.preventDefault() impide el envío por defecto del navegador para que el componente controle totalmente el flujo. Con TypeScript la validación se expresa con funciones puras que reciben la forma del formulario y devuelven un diccionario de errores tipado.
interface RegistroErrores {
nombre?: string;
email?: string;
edad?: string;
}
function validar(form: RegistroForm): RegistroErrores {
const errores: RegistroErrores = {};
if (!form.nombre.trim()) errores.nombre = "El nombre es obligatorio";
if (!form.email.includes("@")) errores.email = "Email no válido";
if (form.edad < 18) errores.edad = "Debes ser mayor de edad";
return errores;
}
El componente almacena los errores en otro useState<RegistroErrores> y los renderiza junto a cada control. El tipado garantiza que no haya errores de tipo entre el formulario y su validación, y que cualquier cambio en la forma del formulario obligue a actualizar la función validar.
Delegar handlers tipados a componentes hijos
Cuando extraes un campo en un componente reutilizable, el manejador del cambio se convierte en una prop. Tiparlo correctamente es fundamental para que el consumidor no tenga que adivinar la firma.
import type { ChangeEvent } from "react";
interface CampoTextoProps {
nombre: string;
etiqueta: string;
valor: string;
onCambio: (event: ChangeEvent<HTMLInputElement>) => void;
}
export function CampoTexto({ nombre, etiqueta, valor, onCambio }: CampoTextoProps) {
return (
<label>
{etiqueta}
<input name={nombre} value={valor} onChange={onCambio} />
</label>
);
}
El padre mantiene el estado y el hijo solo propaga el evento, con el tipo ChangeEvent<HTMLInputElement> explícito en la prop onCambio. Este patrón es la base de cualquier librería de formularios interna: cada campo es un componente tipado y el estado del formulario se centraliza arriba. Cuando un equipo de consultoría entrega una librería corporativa de componentes, estos tipos forman parte del contrato que el cliente verá en su IDE al autocompletar.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, React es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de React
Explora más contenido relacionado con React y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Tipar manejadores de eventos con los genéricos de React (ChangeEvent, MouseEvent, FormEvent). Construir formularios controlados con estado tipado. Validar envíos con preventDefault. Delegar handlers tipados a componentes hijos.