Pular para o conteúdo
TypeScript

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 interface (ou type) e passar como generic do componente. O nome convencional é NomeComponenteProps. 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 extends, 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 props e acessar props.nome, você desestrutura: ({ nome, idade }: MinhaProps). 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

  1. Crie uma interface com o sufixo Props — Declare cada prop com seu tipo. Use ? para opcionais. Exemplo: interface BotaoProps { texto: string; onClick: () => void; cor?: string; }
  2. Passe a interface como tipo do parâmetro — Desestruture direto: function Botao({ texto, onClick, cor = "blue" }: BotaoProps). Valores default substituem o undefined de props opcionais.
  3. Tipe a prop children quando necessário — Use React.ReactNode para aceitar qualquer conteúdo renderizável. Ou React.PropsWithChildren<MinhaProps> que já inclui children automaticamente.
  4. Estenda interfaces para composição — Componentes que compartilham props base usam extends: interface BotaoIconeProps extends BotaoProps { icone: React.ReactNode; }
  5. Use HTMLAttributes para props nativas — Quando o componente é um wrapper de elemento HTML, estenda de React.HTMLAttributes<HTMLDivElement> pra herdar todas as props nativas.

Exemplos práticos: children, composição e eventos

Tipando children

A prop children aceita qualquer coisa renderizável no React. O tipo certo é React.ReactNode: 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 <button> nativo (onClick, disabled, type, etc.). Estender React.ButtonHTMLAttributes 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" => deletar()}>
  Deletar
</BotaoCustom>

Callback props tipadas

Props que são funções de callback precisam declarar os parâmetros e retorno. Nada de Function 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 => onSelecionar(id)}>
      {titulo}
      {onRemover && (
        <button => 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

  • Criei interface/type com sufixo Props para cada componente
  • Props obrigatórias não têm ? e opcionais têm
  • Children tipado como React.ReactNode quando necessário
  • Callbacks declaram parâmetros e tipo de retorno
  • Usei extends ou & para compor interfaces com props compartilhadas
  • Props de elementos HTML estendem de React.HTMLAttributes
  • Destructuring no parâmetro com valores default para opcionais
  • Nenhuma prop usa any ou Function genérico

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.