React con TypeScript
Desarrollo React con tipos seguros — props de componentes, hooks, eventos, contexto y patrones genéricos con TypeScript.
React con TypeScript
TypeScript agrega verificación de tipos estática a tu código React, detectando errores en tiempo de compilación en lugar de tiempo de ejecución. Esta guía cubre los patrones esenciales para tipar componentes, hooks, eventos y estado en proyectos modernos de React + TypeScript.
1. Tipar Props de Componentes
interface BotonProps {
etiqueta: string;
variante?: "primary" | "secondary" | "danger";
deshabilitado?: boolean;
onClick: () => void;
}
function Boton({ etiqueta, variante = "primary", deshabilitado, onClick }: BotonProps) {
return (
<button className={`btn btn-${variante}`} disabled={deshabilitado} onClick={onClick}>
{etiqueta}
</button>
);
}
2. Props con Children
interface TarjetaProps {
titulo: string;
children: React.ReactNode;
}
function Tarjeta({ titulo, children }: TarjetaProps) {
return (
<div className="card">
<h2>{titulo}</h2>
<div>{children}</div>
</div>
);
}
3. Tipar useState
// Inferido como string
const [nombre, setNombre] = useState("");
// Tipo explícito — inicial null, luego un objeto
interface Usuario {
id: number;
nombre: string;
email: string;
}
const [usuario, setUsuario] = useState<Usuario | null>(null);
// Acceso con narrowing
if (usuario) {
console.log(usuario.nombre); // TypeScript sabe que usuario es Usuario
}
4. Tipar Eventos
function EjemplosEventos() {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log("Click en:", e.clientX, e.clientY);
};
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<button onClick={handleClick}>Enviar</button>
</form>
);
}
5. Tipar Contexto
interface AuthContextType {
usuario: Usuario | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
function useAuth(): AuthContextType {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth debe usarse dentro de un AuthProvider");
}
return context;
}
6. Componentes Genéricos
interface ListaProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string | number;
}
function Lista<T>({ items, renderItem, keyExtractor }: ListaProps<T>) {
return (
<ul>
{items.map((item) => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
);
}
// Uso — TypeScript infiere T como Usuario
<Lista<Usuario>
items={usuarios}
keyExtractor={(u) => u.id}
renderItem={(u) => <span>{u.nombre}</span>}
/>
7. Tipos de Utilidad para React
// Extender props de elementos HTML nativos
type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
label: string;
error?: string;
};
function Input({ label, error, ...inputProps }: InputProps) {
return (
<div>
<label>{label}</label>
<input {...inputProps} />
{error && <span className="text-red-500">{error}</span>}
</div>
);
}
⚠️ Errores Comunes
- • No uses
React.FC— incluye children implícitamente. Tipa props directamente. - • No uses
anypara eventos — usa los tipos de eventos específicos de React. - • No olvides el narrowing de tipos nullables —
useState<User | null>requiere verificaciones de null. - • Evita aserciones de tipo (
as) — prefiere type guards y tipado apropiado.