Como tipar o useState do React com TypeScript
Neste artigo você aprenderá a como tipar o useState do React, utilizando o superset de JavaScript: o TypeScript!

Fala programador(a), beleza? Bora aprender mais sobre React JS!
TypeScript trouxe ao mundo JavaScript uma dimensão adicional: a tipagem estática.
E com a popularização do React, era apenas uma questão de tempo até essas duas poderosas ferramentas convergirem.
Uma área em que vemos essa intersecção é ao usar hooks, especificamente o useState, no React com TypeScript.
Neste artigo, vamos explorar como você pode tipar com precisão o hook useState do React ao trabalhar com TypeScript, permitindo que você tire o máximo proveito de ambos.
Por que tipar o useState?
O TypeScript é um superconjunto do JavaScript que permite a adição de tipos. Isso pode parecer um pouco desnecessário no início, mas quando sua aplicação cresce, os benefícios se tornam inestimáveis.
Com a tipagem, você pode evitar muitos erros em tempo de compilação que, de outra forma, só seriam detectados em tempo de execução.
Ao usar o useState com TypeScript, você está essencialmente fornecendo ao compilador informações sobre o tipo de estado que você está tentando gerenciar.
Isso garante que você não tente, acidentalmente, atualizar o estado com um tipo de dado incompatível.
Começando com a tipagem básica
Vamos começar com um exemplo simples para demonstrar a tipagem básica do useState.
Exemplo:
import React, { useState } from 'react';
const MeuComponente: React.FC = () => {
const [contador, setContador] = useState<number>(0);
return (
<div>
<p>O contador está em: {contador}</p>
<button onClick={() => setContador(contador + 1)}>Incrementar</button>
</div>
);
}
Neste exemplo, estamos usando o useState para gerenciar um contador. Explicitamente declaramos que o estado deve ser do tipo number ao usar <number>.
Lidando com objetos complexos
Na prática, você pode encontrar situações em que o estado que está gerenciando não é tão simples quanto um número ou uma string. Pode ser um objeto com várias propriedades. Vejamos como tipar isso.
Exemplo:
import React, { useState } from 'react';
interface Usuario {
nome: string;
idade: number;
}
const MeuComponente: React.FC = () => {
const [usuario, setUsuario] = useState<Usuario>({ nome: 'João', idade: 30 });
return (
<div>
<p>{usuario.nome} tem {usuario.idade} anos de idade.</p>
</div>
);
}
Aqui, definimos uma interface Usuario e informamos ao useState que ele deve esperar um estado que se ajuste à forma desta interface.
Lidando com estados que podem ser nulos ou indefinidos
Em alguns casos, você pode ter um estado que começa como nulo ou indefinido e depois é preenchido. TypeScript tem você coberto aqui também.
Exemplo:
import React, { useState } from 'react';
const MeuComponente: React.FC = () => {
const [nome, setNome] = useState<string | null>(null);
return (
<div>
{nome ? <p>Olá, {nome}!</p> : <p>Nome não fornecido.</p>}
</div>
);
}
Usando o operador |, informamos ao TypeScript que o estado pode ser uma string ou nulo.
Quer aprender mais sobre programação? Conheça nosso canal no YouTube:
Conclusão
Com a crescente popularidade do TypeScript no desenvolvimento React, é vital que os desenvolvedores entendam como tipar corretamente seus estados para obter o máximo dos benefícios que ambos oferecem.
O hook useState, sendo uma parte fundamental do React, é um excelente ponto de partida para mergulhar no mundo da tipagem no React com TypeScript.
Se você ainda não está usando TypeScript com React, esperamos que este guia sirva como um incentivo para começar.
A segurança e clareza adicionais que você obtém ao tipar seus estados e props farão uma diferença significativa à medida que sua aplicação cresce e se torna mais complexa.
Está buscando em evoluir como Programador? Confira o nossos cursos de programação.
Precisa mesmo do <number>? Entendendo a inferência:
Antes de sair tipando tudo na mão, se liga numa coisa: o TypeScript olha o valor inicial e deduz o tipo sozinho
// contador é number, setContador só aceita numberAqui o <number> seria opcional, o 0 já entrega o recado
Então quando vale a pena escrever o generic?
Quando o valor inicial NÃO conta a história toda:
- quando o estado começa vazio e depois recebe outra coisa
- quando o estado pode ter mais de um tipo (string ou null, por exemplo)
- quando o valor inicial é genérico demais, tipo um array vazio ou um objeto vazio
É a mesma ideia de declarar o retorno de uma função mesmo dando pra inferir: você escreve pra travar a intenção, não pra ensinar o compilador 🙂
Atualizando só uma propriedade do objeto sem quebrar a tipagem:
O exemplo do usuário ali em cima funciona, mas na vida real você quase sempre mexe em um campo por vez
E aqui mora uma pegadinha: o useState não faz merge automático como o setState das classes, o que você passar SUBSTITUI o estado inteiro
setUsuario((anterior) => ({ ...anterior, idade: 31 }))Esse espalhamento do estado anterior é o que mantém o objeto completo e o tipo intacto
Se você mandar só { idade: 31 }, o TypeScript avisa que está faltando nome
E isso é exatamente o tipo de erro que ele existe pra pegar antes de você descobrir em produção 😀
Como tipar um array no useState:
Esse é o caso que mais pega gente de surpresa
Parece inofensivo, mas o array vazio não tem nenhuma pista de tipo dentro dele
Aí quando você tenta adicionar um item, o TypeScript reclama, porque ele entendeu aquilo como um array que nunca vai receber nada
A correção é dizer o que mora dentro do array:
interface Tarefa {
id: number
titulo: string
feita: boolean
}
function adicionar(nova: Tarefa) {
}Tome cuidado com array vazio sem generic, é daquelas coisas que só dão as caras lá na frente
Tipando um estado que vem de uma requisição:
Aqui é onde as três coisas que vimos se juntam: objeto, null e generic
O estado começa vazio porque o dado ainda não chegou, e depois ele vira um objeto completo
interface Usuario {
nome: string
idade: number
}
function Perfil() {
useEffect(() => {
buscarUsuario()
.then((dados) => setUsuario(dados))
.finally(() => setCarregando(false))
if (carregando) return <p>Carregando...</p>
if (!usuario) return <p>Nada por aqui</p>
return <p>{usuario.nome} tem {usuario.idade} anos</p>
}Sacou a jogada? Depois daqueles dois if, o TypeScript já sabe que usuario não é mais nulo, e você acessa usuario.nome sem reclamação nenhuma
Isso chama estreitamento de tipo, e é o motivo real de usar Tipo | null em vez de sair inventando objeto vazio só pra calar o compilador
Como passar o setState para um componente filho:
Cedo ou tarde você vai querer mandar a função de atualizar pra outro componente
E aí bate a dúvida: qual é o tipo dessa função?
O React já exporta ele pronto:
import React, { useState, Dispatch, SetStateAction } from 'react'
interface CampoProps {
valor: string
aoMudar: Dispatch<SetStateAction<string>>
}
function Campo({ valor, aoMudar }: CampoProps) {
return <input value={valor} onChange={(e) => aoMudar(e.target.value)} />
}
function Formulario() {
return <Campo valor={nome} aoMudar={setNome} />
}SetStateAction<string> é o jeito do React dizer: aceito uma string nova OU uma função que recebe a antiga e devolve a nova
Se o filho só precisa avisar um valor e nunca vai usar a forma de função, dá pra fechar mais o contrato e pedir só (valor: string) => void
Fica mais simples de reaproveitar e o filho não fica amarrado ao estado do pai
E aquele React.FC dos exemplos?
Parada rápida pra tirar uma dúvida que sempre aparece: React.FC é só uma forma de tipar o componente, não tem nada a ver com o useState
Dá pra escrever o mesmo componente como função normal e tipar as props direto no parâmetro:
interface Props {
titulo: string
}
function MeuComponente({ titulo }: Props) {
return <h1>{titulo}: {contador}</h1>
}Os dois caminhos funcionam e a tipagem do useState é exatamente a mesma nos dois
Escolha o estilo que o seu projeto já usa e siga ele, o problema não é qual dos dois, é misturar os dois no mesmo código
Perguntas frequentes sobre tipar o useState:
Preciso declarar o tipo em todo useState?
Não, quando o valor inicial já revela o tipo (um número, uma string, um booleano) o TypeScript infere sozinho
O generic entra quando o valor inicial não conta a história toda
Como tipar useState que começa nulo?
Usando união com o tipo esperado: useState<Usuario | null>(null)
Depois é só checar antes de acessar as propriedades
Como tipar um array vazio no useState?
Sem isso, o array vazio não aceita os itens que você tentar adicionar depois
Qual é o tipo da função setState pra passar por props?
Dispatch<SetStateAction<Tipo>>, ambos importados do react
Se o filho não precisa da forma de função, (valor: Tipo) => void já resolve
Dá pra usar useState com TypeScript sem criar interface?
Dá, você pode escrever o formato direto no generic ou usar um type
A interface só deixa mais fácil de reaproveitar o mesmo formato em vários lugares
Leia também
Precisa mesmo do <number>? Entendendo a inferência:
Antes de sair tipando tudo na mão, se liga numa coisa: o TypeScript olha o valor inicial e deduz o tipo sozinho
// contador é number, setContador só aceita numberAqui o <number> seria opcional, o 0 já entrega o recado
Então quando vale a pena escrever o generic?
Quando o valor inicial NÃO conta a história toda:
- quando o estado começa vazio e depois recebe outra coisa
- quando o estado pode ter mais de um tipo (string ou null, por exemplo)
- quando o valor inicial é genérico demais, tipo um array vazio ou um objeto vazio
É a mesma ideia de declarar o retorno de uma função mesmo dando pra inferir: você escreve pra travar a intenção, não pra ensinar o compilador 🙂
Atualizando só uma propriedade do objeto sem quebrar a tipagem:
O exemplo do usuário ali em cima funciona, mas na vida real você quase sempre mexe em um campo por vez
E aqui mora uma pegadinha: o useState não faz merge automático como o setState das classes, o que você passar SUBSTITUI o estado inteiro
setUsuario((anterior) => ({ ...anterior, idade: 31 }))Esse espalhamento do estado anterior é o que mantém o objeto completo e o tipo intacto
Se você mandar só { idade: 31 }, o TypeScript avisa que está faltando nome
E isso é exatamente o tipo de erro que ele existe pra pegar antes de você descobrir em produção 😀
Como tipar um array no useState:
Esse é o caso que mais pega gente de surpresa
Parece inofensivo, mas o array vazio não tem nenhuma pista de tipo dentro dele
Aí quando você tenta adicionar um item, o TypeScript reclama, porque ele entendeu aquilo como um array que nunca vai receber nada
A correção é dizer o que mora dentro do array:
interface Tarefa {
id: number
titulo: string
feita: boolean
}
function adicionar(nova: Tarefa) {
}Tome cuidado com array vazio sem generic, é daquelas coisas que só dão as caras lá na frente
Tipando um estado que vem de uma requisição:
Aqui é onde as três coisas que vimos se juntam: objeto, null e generic
O estado começa vazio porque o dado ainda não chegou, e depois ele vira um objeto completo
interface Usuario {
nome: string
idade: number
}
function Perfil() {
useEffect(() => {
buscarUsuario()
.then((dados) => setUsuario(dados))
.finally(() => setCarregando(false))
if (carregando) return <p>Carregando...</p>
if (!usuario) return <p>Nada por aqui</p>
return <p>{usuario.nome} tem {usuario.idade} anos</p>
}Sacou a jogada? Depois daqueles dois if, o TypeScript já sabe que usuario não é mais nulo, e você acessa usuario.nome sem reclamação nenhuma
Isso chama estreitamento de tipo, e é o motivo real de usar Tipo | null em vez de sair inventando objeto vazio só pra calar o compilador
Como passar o setState para um componente filho:
Cedo ou tarde você vai querer mandar a função de atualizar pra outro componente
E aí bate a dúvida: qual é o tipo dessa função?
O React já exporta ele pronto:
import React, { useState, Dispatch, SetStateAction } from 'react'
interface CampoProps {
valor: string
aoMudar: Dispatch<SetStateAction<string>>
}
function Campo({ valor, aoMudar }: CampoProps) {
return <input value={valor} onChange={(e) => aoMudar(e.target.value)} />
}
function Formulario() {
return <Campo valor={nome} aoMudar={setNome} />
}SetStateAction<string> é o jeito do React dizer: aceito uma string nova OU uma função que recebe a antiga e devolve a nova
Se o filho só precisa avisar um valor e nunca vai usar a forma de função, dá pra fechar mais o contrato e pedir só (valor: string) => void
Fica mais simples de reaproveitar e o filho não fica amarrado ao estado do pai
E aquele React.FC dos exemplos?
Parada rápida pra tirar uma dúvida que sempre aparece: React.FC é só uma forma de tipar o componente, não tem nada a ver com o useState
Dá pra escrever o mesmo componente como função normal e tipar as props direto no parâmetro:
interface Props {
titulo: string
}
function MeuComponente({ titulo }: Props) {
return <h1>{titulo}: {contador}</h1>
}Os dois caminhos funcionam e a tipagem do useState é exatamente a mesma nos dois
Escolha o estilo que o seu projeto já usa e siga ele, o problema não é qual dos dois, é misturar os dois no mesmo código
Perguntas frequentes sobre tipar o useState:
Preciso declarar o tipo em todo useState?
Não, quando o valor inicial já revela o tipo (um número, uma string, um booleano) o TypeScript infere sozinho
O generic entra quando o valor inicial não conta a história toda
Como tipar useState que começa nulo?
Usando união com o tipo esperado: useState<Usuario | null>(null)
Depois é só checar antes de acessar as propriedades
Como tipar um array vazio no useState?
Sem isso, o array vazio não aceita os itens que você tentar adicionar depois
Qual é o tipo da função setState pra passar por props?
Dispatch<SetStateAction<Tipo>>, ambos importados do react
Se o filho não precisa da forma de função, (valor: Tipo) => void já resolve
Dá pra usar useState com TypeScript sem criar interface?
Dá, você pode escrever o formato direto no generic ou usar um type
A interface só deixa mais fácil de reaproveitar o mesmo formato em vários lugares
Leia também
Formações
Formação Vibe Coding
Do Prompt ao Produto: Crie Software Real com IA
- 474 aulas
- 20 projetos
- 39h 27min
Blog | Mais populares

As diferenças de var, let e const

Como fazer redirecionamento com PHP
Neste artigo você vai aprender a como fazer redirecionamento com PHP, utilizaremos abordagens fáceis de entender e de aplicar Fala programador(a), beleza? Bora aprender mais […]

ChatGPT: o que é, como usar, dicas e como acessar login
ChatGPT é uma ferramenta de processamento de linguagem natural (NLP) baseada na arquitetura GPT-3.5, desenvolvida pela OpenAI. Sua criação representa um marco significativo no campo […]
