Tipando componentes React
Sem tipo, um componente aceita qualquer prop maluca sem reclamar — até estourar em produção, longe de onde o erro nasceu.
Você já viu interface pra tipar props na lição de interfaces. Aqui o foco é o que aparece de verdade num componente React do dia a dia: children, eventos de formulário e um hook genérico simples.
Props com interface — revisitando com mais detalhe
interface CardProps {
titulo: string;
destaque?: boolean;
onFechar?: () => void;
}
function Card({ titulo, destaque = false, onFechar }: CardProps) {
return (
<div className={destaque ? "border-blue-500" : "border-gray-200"}>
<h3>{titulo}</h3>
{onFechar && <button onClick={onFechar}>Fechar</button>}
</div>
);
}children — o tipo certo é ReactNode
children pode ser texto, um elemento JSX, uma lista de elementos, ou nada — undefined quando o componente é usado sem filhos. ReactNode é o tipo do React que já cobre todos esses casos:
import type { ReactNode } from "react";
interface PainelProps {
titulo: string;
children: ReactNode; // aceita string, <span>, array de elementos, null...
}
function Painel({ titulo, children }: PainelProps) {
return (
<section>
<h2>{titulo}</h2>
{children}
</section>
);
}
<Painel titulo="Resumo">Só um texto simples.</Painel>
<Painel titulo="Detalhes"><ul><li>Item 1</li></ul></Painel>Tipando evento de formulário
Sem anotar o tipo do parâmetro de um handler de evento, o TS não sabe o que event.target contém. O React exporta tipos específicos por tipo de elemento e evento:
import { useState, type ChangeEvent } from "react";
function CampoBusca() {
const [valor, setValor] = useState("");
function lidarComMudanca(event: ChangeEvent<HTMLInputElement>) {
setValor(event.target.value); // TS sabe que .value existe e é string
}
return <input value={valor} onChange={lidarComMudanca} />;
}Um hook genérico simples
Generics também são úteis dentro de hooks customizados — um useToggle ou um useLocalStorage genérico funcionam pra qualquer tipo de estado, sem perder a checagem:
import { useState } from "react";
function useLocalStorage<T>(chave: string, valorInicial: T) {
const [valor, setValor] = useState<T>(() => {
const salvo = localStorage.getItem(chave);
return salvo ? (JSON.parse(salvo) as T) : valorInicial;
});
function atualizar(novoValor: T) {
setValor(novoValor);
localStorage.setItem(chave, JSON.stringify(novoValor));
}
return [valor, atualizar] as const;
}
const [tema, setTema] = useLocalStorage<"claro" | "escuro">("tema", "claro");
setTema("escuro"); // ok
setTema("azul");
// Error: Argument of type '"azul"' is not assignable to parameter of type '"claro" | "escuro"'.Exercício
Escreva o handler lidarComSelecao de um <select onChange={...}> que só precisa logar o valor selecionado no console, tipando o parâmetro do evento corretamente.