Como Tipar Props no React com TypeScript
Tipar props React com TypeScript: children, opcionais e o que não espalhar com any.
Por que isso é importante
Tipar props no React com TypeScript começa por objeto de props explícito e children tipado. any nas props apaga o valor do TS no componente.
Tipando props com interface
A forma padrão de tipar props no React com TypeScript é criar uma <code>interface</code> (ou <code>type</code>) e passar como generic do componente. O nome convencional é <code>NomeComponenteProps</code>. Isso deixa claro o contrato do componente: quais dados ele espera e quais são opcionais.
Dá pra usar interface ou type pra definir props -- os dois funcionam. Interface é mais comum em projetos React porque suporta extensão com <code>extends</code>, o que ajuda quando componentes compartilham props base. Mas não existe regra rígida: use o que o time preferir.
O destructuring direto no parâmetro da função é o padrão mais limpo. Ao invés de receber <code>props</code> e acessar <code>props.nome</code>, você desestrutura: <code>({ nome, idade }: MinhaProps)</code>. O TypeScript valida cada campo na hora.
// Definindo a interface de props
interface CardUsuarioProps {
nome: string;
email: string;
avatar?: string; // opcional
ativo?: boolean; // opcional
}
// Componente com destructuring tipado
function CardUsuario({ nome, email, avatar, ativo = true }: CardUsuarioProps) {
return (
<div className={ativo ? "card-ativo" : "card-inativo"}>
{avatar && <img src={avatar} alt={nome} />}
<h3>{nome}</h3>
<p>{email}</p>
</div>
);
}
// Uso - TypeScript valida na hora
<CardUsuario nome="Ana" email="ana@dev.com" />
// <CardUsuario nome="Ana" /> // Erro: faltou email
Passo a passo: tipando props do zero
? para opcionais. Exemplo: interface BotaoProps { texto: string; onClick: () => void; cor?: string; }function Botao({ texto, onClick, cor = "blue" }: BotaoProps). Valores default substituem o undefined de props opcionais.React.ReactNode para aceitar qualquer conteúdo renderizável. Ou React.PropsWithChildren<MinhaProps> que já inclui children automaticamente.extends: interface BotaoIconeProps extends BotaoProps { icone: React.ReactNode; }React.HTMLAttributes<HTMLDivElement> pra herdar todas as props nativas.Exemplos práticos: children, composição e eventos
Tipando children
A prop <code>children</code> aceita qualquer coisa renderizável no React. O tipo certo é <code>React.ReactNode</code>: strings, números, elementos JSX, arrays, fragments, null -- tudo passa.
interface ContainerProps {
children: React.ReactNode;
larguraMaxima?: string;
}
function Container({ children, larguraMaxima = "800px" }: ContainerProps) {
return (
<div style={{ maxWidth: larguraMaxima, margin: "0 auto" }}>
{children}
</div>
);
}
// Uso
<Container larguraMaxima="1200px">
<h1>Título</h1>
<p>Conteúdo aqui dentro</p>
</Container>
Estendendo props nativas do HTML
Galera, quando você cria um botão customizado, quer que ele aceite todas as props de um <code><button></code> nativo (onClick, disabled, type, etc.). Estender <code>React.ButtonHTMLAttributes</code> resolve isso sem listar cada prop manualmente.
interface BotaoCustomProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variante?: "primario" | "secundario" | "perigo";
carregando?: boolean;
}
function BotaoCustom({
variante = "primario",
carregando = false,
children,
...rest // todas as props nativas do button
}: BotaoCustomProps) {
return (
<button
className={`btn btn-${variante}`}
disabled={carregando}
{...rest}
>
{carregando ? "Carregando..." : children}
</button>
);
}
// Uso - aceita onClick, disabled, type...
<BotaoCustom variante="perigo" onClick={() => deletar()}>
Deletar
</BotaoCustom>
Callback props tipadas
Props que são funções de callback precisam declarar os parâmetros e retorno. Nada de <code>Function</code> genérico -- isso mata o autocomplete e esconde bugs.
interface ListaItemProps {
id: number;
titulo: string;
onSelecionar: (id: number) => void;
onRemover?: (id: number) => Promise<void>;
}
function ListaItem({ id, titulo, onSelecionar, onRemover }: ListaItemProps) {
return (
<li onClick={() => onSelecionar(id)}>
{titulo}
{onRemover && (
<button onClick={() => onRemover(id)}>X</button>
)}
</li>
);
}
Erros comuns ao tipar props
Evite esses deslizes
Usar any nas props: perde toda proteção. Se não sabe o tipo exato, use unknown e faça narrowing.
Esquecer de tipar children: se o componente renderiza children mas não declara na interface, TypeScript reclama quando você tenta passar conteúdo.
Usar React.FC sem necessidade: o React.FC adiciona children implicitamente e tem comportamento inconsistente. Prefira tipagem direta no parâmetro.
Não usar extends para composição: copiar props entre interfaces gera duplicação. Use extends ou intersection (&) pra compor.
Callback tipada como Function: o tipo Function aceita qualquer coisa. Sempre declare (param: tipo) => retorno pra cada callback.
Checklist: Props tipadas corretamente
Checklist de Props React + TypeScript
Construa componentes profissionais
Props bem tipadas são o alicerce de todo componente React profissional. No CrazyStack, você cria um design system inteiro com TypeScript -- cada componente tipado, testável e reutilizável. Do botão ao formulário completo, tudo com tipagem que o mercado exige.