Por que TypeScript no React
Em projetos profissionais, o React quase sempre é escrito com TypeScript. A
vantagem é enorme: o editor avisa sobre erros antes de executar (uma prop
mal escrita, um valor que pode ser undefined, um handler com o tipo
errado), e o código se autodocumenta. Para o React, o mais importante é
tipar as props, o estado e os eventos.
Nota: o TypeScript é compilado para JavaScript; no navegador sempre acaba rodando JS. Aqui veremos a teoria com exemplos
.tsx; os exercícios práticos deste módulo são escritos em JS válido.
Tipar as props com interface ou type
As props de um componente são um objeto, então descrevemos seu formato com uma
interface (ou um type). Depois a usamos para tipar o parâmetro:
interface SaudacaoProps {
nome: string;
idade?: number; // o "?" a torna opcional
}
function Saudacao({ nome, idade }: SaudacaoProps) {
return <h1>Olá, {nome}{idade ? ` (${idade})` : ""}</h1>;
}
nome: string→ obrigatória. Se você esquecê-la ao usar<Saudacao />, erro na compilação.idade?: number→ opcional. Dentro do componente,idadeénumber | undefined.
interface e type são quase intercambiáveis para props; use o que sua
equipe preferir. type também permite uniões (type Estado = "ok" | "erro").
Componentes: função tipada ou React.FC
A forma recomendada hoje é uma função normal com as props tipadas (como
acima). Também existe React.FC, que tipa o componente inteiro e inclui
children de forma implícita em versões antigas:
const Botao: React.FC<{ texto: string }> = ({ texto }) => {
return <button>{texto}</button>;
};
No React moderno se prefere a função tipada em vez de
React.FC, porque é mais explícita (você declarachildrensó se precisar) e mais flexível.
Estado tipado: useState<T>
useState infere o tipo a partir do valor inicial:
const [nome, setNome] = useState(""); // string
const [ativo, setAtivo] = useState(false); // boolean
Quando o valor inicial não basta para inferir (p. ex. começa em null mas depois
guardará um objeto), você anota explicitamente com useState<T>:
interface Usuario { id: number; nome: string; }
const [usuario, setUsuario] = useState<Usuario | null>(null);
Tipar eventos
Os manipuladores de eventos recebem um evento do React, não do DOM nativo. Os
tipos vivem no espaço React.*:
function Campo() {
const [texto, setTexto] = useState("");
function aoDigitar(e: React.ChangeEvent<HTMLInputElement>) {
setTexto(e.target.value);
}
function aoEnviar(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
}
return (
<form onSubmit={aoEnviar}>
<input value={texto} onChange={aoDigitar} />
</form>
);
}
Os mais comuns: React.ChangeEvent<HTMLInputElement> (inputs),
React.MouseEvent<HTMLButtonElement> (cliques), React.FormEvent (formulários) e
React.KeyboardEvent (teclado). Se você escrever o handler em linha
(onClick={(e) => ...}), o TypeScript geralmente infere o tipo do evento por você.
children e React.ReactNode
children é "o que vai entre as tags de abertura e fechamento" do componente.
Como pode ser texto, um número, um elemento JSX, uma lista deles ou nada, seu
tipo é React.ReactNode: o tipo mais geral de "qualquer coisa que o React
pode renderizar".
interface CartaoProps {
titulo: string;
children: React.ReactNode;
}
function Cartao({ titulo, children }: CartaoProps) {
return (
<section>
<h2>{titulo}</h2>
<div>{children}</div>
</section>
);
}
// Uso:
<Cartao titulo="Olá">
<p>Qualquer conteúdo vai aqui.</p>
</Cartao>
Regra mental: use
React.ReactNodeparachildrene para qualquer prop que receba "conteúdo renderizável". Para uma prop que seja um elemento específico, existeReact.ReactElement.