TSX e a Ascensão dos Componentes Frontend Tipados
Você está construindo um componente React. Você passa uma string onde deveria ser um número. O TypeScript detecta o erro antes do navegador. Esta interação simples—tipos prevenindo erros em tempo de execução—explica por que TSX se tornou o formato padrão para o desenvolvimento frontend moderno.
Este artigo aborda o que componentes frontend tipados realmente significam na prática: tipagem de props, manipulação de eventos, padrões de children, e como a inferência do TypeScript elimina código repetitivo. Também abordaremos como a divisão entre componentes de servidor e cliente do React 19 afeta sua estratégia de tipagem.
Principais Conclusões
- Componentes tipados em TSX detectam incompatibilidades de tipo em tempo de compilação, reduzindo erros em tempo de execução antes que o código chegue ao navegador.
- A inferência do TypeScript lida automaticamente com a maioria das tipagens de eventos—anotações explícitas só são necessárias ao extrair handlers para funções separadas.
- Uniões discriminadas permitem componentes type-safe com comportamentos mutuamente exclusivos, onde o TypeScript restringe tipos com base em uma propriedade discriminante.
- A fronteira servidor/cliente do React 19 introduz restrições de serialização: props passadas de componentes de servidor para cliente devem ser serializáveis (sem funções, classes, símbolos ou outros valores não serializáveis).
O Que Componentes Tipados em TSX Realmente Significam
Um componente tipado não é apenas um componente com props tipadas. É um componente onde o TypeScript compreende todo o contrato: o que entra, o que sai, e o que acontece no meio.
Com a transformação JSX moderna (react-jsx), você não precisa importar React em cada arquivo. Você escreve TSX, e a toolchain cuida do resto. O Vite configura isso corretamente de forma automática:
interface ButtonProps {
label: string
disabled?: boolean
}
function Button({ label, disabled = false }: ButtonProps) {
return <button disabled={disabled}>{label}</button>
}
Note que não há React.FC aqui. Esse padrão é opcional—e frequentemente desnecessário. Tipar props diretamente no parâmetro da função é mais limpo e evita a prop implícita children que React.FC adiciona, mesmo que você não a queira.
Tipagem de Props e Segurança de Tipos em React TSX
A tipagem de props é onde a maioria dos desenvolvedores começa, mas a segurança de tipos em React TSX vai mais fundo. Considere uniões discriminadas para componentes com comportamentos mutuamente exclusivos:
type LinkButtonProps =
| { variant: 'button'; onClick: () => void }
| { variant: 'link'; href: string }
function LinkButton(props: LinkButtonProps) {
if (props.variant === 'link') {
return <a href={props.href}>Navigate</a>
}
return <button onClick={props.onClick}>Click</button>
}
O TypeScript restringe o tipo com base em variant. Você não pode acessar acidentalmente href quando variant é 'button'. Este padrão escala bem para APIs de componentes complexas.
Tipagem de Eventos Sem Código Repetitivo
Manipuladores de eventos frequentemente confundem desenvolvedores novos no TypeScript. A boa notícia: a inferência lida automaticamente com a maioria dos casos.
import { useState } from 'react'
function SearchInput() {
const [query, setQuery] = useState('')
// TypeScript infere o tipo do evento pelo contexto
return (
<input
value={query}
onChange={(e) => setQuery(e.currentTarget.value)}
/>
)
}
Quando você extrai handlers para funções separadas, precisará de tipos explícitos:
import type { ChangeEvent } from 'react'
function handleChange(event: ChangeEvent<HTMLInputElement>) {
// event.currentTarget.value é corretamente tipado como string
}
Discover how at OpenReplay.com.
Tipagem de Children: ReactNode vs ReactElement
Para children, React.ReactNode cobre o caso comum—qualquer coisa renderizável:
import type { ReactNode } from 'react'
interface CardProps {
children: ReactNode
}
Use React.ReactElement quando você precisar estritamente de elementos JSX, excluindo strings e números. Para componentes que aceitam children, PropsWithChildren é uma alternativa mais limpa do que adicionar manualmente a prop children:
import type { PropsWithChildren } from 'react'
interface CardProps {
title: string
}
function Card({ title, children }: PropsWithChildren<CardProps>) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
)
}
Tipagem de Componentes de Servidor e Cliente no React 19
Projetos React 19 com TypeScript, especialmente aqueles usando Next.js App Router, dividem componentes em fronteiras de servidor e cliente. Isso afeta a tipagem.
Componentes de servidor podem ser assíncronos e buscar dados diretamente:
// Server Component - nenhuma diretiva necessária (padrão no App Router)
async function UserProfile({ userId }: { userId: string }) {
const user = await fetchUser(userId)
return <div>{user.name}</div>
}
Componentes de cliente requerem a diretiva 'use client' e podem usar hooks:
'use client'
import { useState } from 'react'
interface CounterProps {
initialCount: number
}
function Counter({ initialCount }: CounterProps) {
const [count, setCount] = useState(initialCount)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
A fronteira de tipagem importa: props passadas de componentes de servidor para cliente devem ser serializáveis. Funções, classes, símbolos e valores não serializáveis (como instâncias de classe ou Dates) não atravessam essa fronteira.
Form Actions e Fluxos de Trabalho Modernos Tipados
O React 19 introduz server actions (comumente usadas com formulários) como um padrão tipado para lidar com submissões, tipicamente definidas em arquivos de servidor e invocadas a partir de componentes cliente:
async function submitForm(formData: FormData) {
'use server'
const email = formData.get('email')
if (typeof email !== 'string') {
throw new Error('Email is required')
}
// Processar submissão
}
Isso se integra com componentes frontend tipados mantendo o tratamento de formulários type-aware do cliente ao servidor.
Conclusão
Componentes tipados em TSX reduzem bugs ao detectar incompatibilidades em tempo de compilação. A inferência do TypeScript minimiza código repetitivo—você raramente precisa de anotações de tipo explícitas para hooks ou handlers inline. A divisão servidor/cliente no React 19 adiciona uma nova consideração: fronteiras de serialização.
Evite React.FC a menos que tenha uma razão específica. Tipe props diretamente. Deixe a inferência trabalhar. Seu editor agradecerá com autocomplete preciso e feedback imediato de erros.
Perguntas Frequentes
Tipe props diretamente nos parâmetros da função. React.FC adiciona uma prop children implícita mesmo que você não a queira, e tipar props diretamente é mais limpo. A equipe do React não recomenda React.FC como padrão. Use-o apenas quando você precisar especificamente do seu comportamento, como ao trabalhar com codebases legadas que o esperam.
Para handlers inline, o TypeScript infere o tipo do evento automaticamente pelo contexto. Quando você extrai handlers para funções separadas, use tipos explícitos como React.ChangeEvent para mudanças em inputs ou React.MouseEvent para cliques. O parâmetro genérico especifica o tipo do elemento, como HTMLInputElement ou HTMLButtonElement.
ReactNode aceita qualquer coisa renderizável incluindo strings, números, null, undefined e elementos JSX. ReactElement aceita apenas elementos JSX, excluindo primitivos. Use ReactNode para a maioria dos casos. Use ReactElement quando seu componente especificamente requer children JSX e deve rejeitar texto simples ou números.
Não. Props passadas de componentes de servidor para cliente devem ser serializáveis. Funções, classes, símbolos e outros valores não serializáveis não podem atravessar a fronteira servidor-cliente. Defina manipuladores de eventos e callbacks dentro de componentes cliente, ou use server actions para submissões de formulário que precisam de processamento no servidor.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.