TypeScript: quando usar unknown em vez de any?

Você já sentiu aquela pontada de dúvida ao receber um retorno de uma API ou ao lidar com uma biblioteca externa?
Nessas horas, o any parece um abraço quentinho: ele aceita tudo, não reclama de nada e deixa o código rodar. Mas, como diz o ditado no mundo dev, “com grandes poderes vêm grandes responsabilidades” (e, às vezes, grandes erros de runtime).
Podemos pensar no any e no unknown como dois guias em uma floresta desconhecida. O any é aquele guia excessivamente otimista que diz: “Pode pisar em qualquer lugar, está tudo bem!”, até que você pisa em um buraco.
Já o unknown é o guia cauteloso: ele para você na frente de cada arbusto e diz: “Não dê um passo até verificarmos se o chão é firme.”
Manter a tipagem segura não é apenas um “capricho” técnico. No mercado atual, onde sistemas escaláveis e colaboração entre grandes times são a regra, a clareza do código é o que separa um projeto de sucesso de um pesadelo de manutenção.
O unknown é o “irmão responsável” do any, e ele permite que você mantenha a flexibilidade de dados dinâmicos sem abrir mão da proteção que o TypeScript oferece.
O que é o tipo any?
O any é, essencialmente, a “porta de saída” do sistema de tipos do TypeScript. Quando você atribui any a uma variável, você está dizendo ao compilador: “Ei, confie em mim, eu sei o que estou fazendo. Não precisa validar nada aqui”.
É o tipo mais flexível da linguagem, pois ele representa literalmente qualquer valor e permite que você acesse qualquer propriedade ou método sem questionamentos.
Na prática, o any costuma aparecer em três cenários principais:
- Migração de JavaScript para TypeScript: Quando o volume de erros é tão grande que o “tipar tudo com any” parece a única saída para fazer o código compilar.
- Pressa no desenvolvimento: Aquele famoso “depois eu arrumo” que acaba ficando para sempre no repositório.
- Dados de origens incertas: Quando a pessoa desenvolvedora não tem certeza da estrutura que virá de um JSON externo.
O grande risco aqui é o efeito cascata. Se uma função retorna any, qualquer variável que receba esse valor também perde a proteção de tipo. Em pouco tempo, você terá um código TypeScript que se comporta como JavaScript puro, onde erros como TypeError: ‘x’ is not a function assombram as suas noites.
Da uma olhada nesse exemplo propositalmente absurdo:
function formataTexto(nome: string): any {
return `Olá, ${nome}`;
}
const listaDeExemplo: number[] = formataTexto("estudante");
console.log(listaDeExemplo);
console.log(typeof listaDeExemplo);
listaDeExemplo.push(42);O TypeScript não acusa erro. Mas em tempo de execução, o código quebra. Por quê? Porque a variável listaDeExemplo no momento do push já não é mais um number[]. É uma string. Ou seja: o any permitiu que eu mentisse para o sistema de tipos, e ele acreditou.
E o resultado? Bem...
![“Captura do VS Code mostrando código TypeScript: função formataTexto retorna uma string, atribuída a variável tipada como number[]. No terminal, aparecem “Olá, estudante”, “string” e erro TypeError: listaDeExemplo.push is not a function ao tentar usar push em uma string.”](https://cdn-wcsm.alura.com.br/2026/05/05193202/image.png)
É exatamente o tipo de problema que o TypeScript existe para evitar. No fim das contas, any não é apenas “um tipo flexível”, ele é a desativação do TypeScript naquele ponto do código.
O que é o tipo unknown?
O unknown é o que chamamos de tipo universal seguro. Assim como o any, você pode atribuir qualquer valor a uma variável do tipo unknown. No entanto, a semelhança para por aí. A grande diferença é que o TypeScript não permite que você faça nada com um valor unknown até que você verifique o que ele é.

A regra de ouro aqui é a cautela: você sabe que algo existe ali dentro, pode ser uma string, um objeto ou um número, por exemplo, mas você não sabe o que é (ainda).
Até mesmo tentar acessar a propriedade length em um unknown resultará em um erro de compilação.
A semântica do “Eu não sei”
Usar unknown comunica uma intenção clara para outras pessoas devs: “Eu recebi esse dado de algum lugar e não posso garantir sua estrutura agora”. É uma forma honesta de programar. Em vez de fingirmos que tudo está sob controle (como o any faz), admitimos a incerteza e preparamos o terreno para uma validação segura.
Como implementar na prática?
Agora que já entendemos a teoria, vamos colocar a mão na massa. O segredo para dominar o unknown é entender o conceito de Narrowing (estreitamento, afunilamento). Basicamente, é o processo de transformar um tipo genérico e desconhecido em algo específico e utilizável.
Quando trabalhamos com unknown, o desafio não é escrever o if, o desafio é não saber o que pode chegar, e é exatamente isso que diferencia esse cenário de quando usamos unions.
Cenário A: Tipagem restrita (Unions)
Imagine que você está criando uma função que recebe dados que podem ser:
- uma string
- um array de strings
- ou null
Aqui você já conhece todas as possibilidades. O TypeScript consegue acompanhar o seu raciocínio conforme você trata cada caso.
function imprimirLista(dados: string | string[] | null) {
if (Array.isArray(dados)) {
dados.forEach(dado => console.log(dado));
}
else if (typeof dados === "string") {
console.log(dados.toUpperCase());
}
}Nesse cenário, o narrowing acontece de forma natural.
O compilador entende:
- se passou pela verificação de array, então só pode ser string[]
- se passou pela verificação de string, então só pode ser string
Existe uma lista fechada de possibilidades e isso torna o código previsível e mais fácil de modelar.
Cenário B: O desafio do unknown
Agora imagine um outro caso: você recebe um dado desconhecido, pode ser o valor do localStorage, o resultado de um JSON.parse(), o retorno de uma biblioteca, um evento de usuário, etc.
Nesse momento você não tem nenhuma garantia sobre o formato desse dado.
É aí que entra o unknown.
function processarDesconhecido(valor: unknown) {
if (Array.isArray(valor)) {
valor.forEach(item => console.log(item));
}
else if (typeof valor === "string") {
console.log(valor.trim());
}
}Aqui o TypeScript não possui uma lista prévia de tipos possíveis, ele apenas sabe que esse valor pode ser qualquer coisa. Por isso ele exige que você investigue o dado antes de utilizá-lo.
Cada verificação funciona como uma etapa de validação:
- Primeiro testamos se o valor é um array
- Caso não seja, testamos se é uma string
Quando uma dessas condições é verdadeira, o tipo é estreitado dentro daquele bloco, ou seja:
- antes → unknown
- depois → um tipo específico e utilizável
Esse é o narrowing acontecendo na prática.
Quando o narrowing simples não é suficiente
Até agora usamos verificações básicas como o typeof e o Array.isArray, e isso funciona muito bem para tipos primitivos ou estruturas mais simples. Mas e quando estamos lidando com objetos complexos?
Por exemplo:
- um usuário
- um perfil
- um produto
- um payload de API
Nesse caso precisamos de uma forma mais estruturada de validar o tipo.
Cenário C: Recebendo dados de APIs externas
Ao consumir uma API, não temos controle total sobre o que o servidor envia (especialmente se for um serviço de terceiros). Em vez de tipar o retorno com any, use unknown.
async function buscarEndereco(cep: string): Promise<unknown> {
const resposta = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
if (!resposta.ok) {
throw new Error("Erro ao buscar CEP");
}
return resposta.json();
}
const resultado = await buscarEndereco("01001000");Aqui o TypeScript impede o acesso direto às propriedades porque resultado é unknown. Isso nos força a validar antes de usar.
O uso de Type Guards
Para “desbloquear” o valor dentro de um unknown, usamos funções chamadas Type Guards. Elas verificam o tipo em tempo de execução e informam ao TypeScript que o valor é seguro.
Uma resposta típica da API ViaCEP contém campos como:
{
"cep": "01001-000",
"logradouro": "Praça da Sé",
"bairro": "Sé",
"localidade": "São Paulo",
"uf": "SP"
}Podemos criar uma interface e um Type Guard:
interface EnderecoViaCEP {
cep: string;
logradouro: string;
bairro: string;
localidade: string;
uf: string;
}
function ehEnderecoViaCEP(dado: unknown): dado is EnderecoViaCEP {
return (
typeof dado === "object" &&
dado !== null &&
"cep" in dado &&
"logradouro" in dado &&
"bairro" in dado &&
"localidade" in dado &&
"uf" in dado
);
}
async function exemplo() {
const resultado = await buscarEndereco("01001000");
if (ehEnderecoViaCEP(resultado)) {
console.log(resultado.logradouro);
console.log(resultado.localidade);
} else {
console.log("Resposta inesperada da API");
}
}O que está acontecendo aqui?
- A interface como contrato: Criamos a interface EnderecoViaCEP para definir como esperamos que os dados sejam. É o nosso “cenário ideal”.
- O parâmetro unknown: Note que na função ehEnderecoViaCEP, o dado entra como unknown. Isso é honesto: antes da validação, não podemos garantir que a API não retornou um erro ou um formato vazio.
- O predicado de tipo: O segredo está no retorno dado is EnderecoViaCEP. Assim dizemos ao TypeScript: “Se esta função retornar true, você pode confiar que dado possui todas as propriedades da interface”.
- A checagem de segurança: Dentro do if, o TypeScript faz o narrowing. Note que você pode digitar resultado. e o VS Code vai sugerir “logradouro” ou “localidade” com total confiança. Se o if falhar (caso a API mude o formato, por exemplo), caímos no else e evitamos que o sistema quebre tentando ler uma propriedade que não existe.
Conclusão e próximos passos
Chegamos ao fim da nossa jornada entre o permissivo e o seguro. Entender a diferença entre any e unknown é um divisor de águas na carreira de qualquer pessoa que desenvolve com TypeScript.
Ao escolher o unknown, você não está apenas silenciando o compilador, você está assumindo o compromisso de escrever um código mais resiliente, legível e profissional.
É uma mudança de mentalidade que transforma o "programar por tentativa e erro" em "programar com confiança", aplicando conceitos de Clean Code desde a base da sua aplicação.
Essa mesma mentalidade é exatamente o que a Carreira de Desenvolvimento Front-End React propõe: uma evolução estruturada que vai além de “fazer funcionar”, preparando você para construir aplicações robustas, aplicar boas práticas arquiteturais e tomar decisões técnicas com confiança.
Ao dominar tipagem, testes, performance e padrões modernos dentro do ecossistema React, você transforma conhecimento em maturidade profissional, dando passos consistentes rumo à autonomia e, no futuro, à liderança técnica.
FAQ | Perguntas frequentes sobre o tipo Unknown
1. O unknown substitui o any em 100% dos casos?
Quase todos. O any ainda tem seu lugar em testes rápidos ou prototipagem inicial. Para produção, prefira sempre o unknown.
2. Usar unknown deixa o código mais lento?
Não. O TypeScript é removido na compilação. O custo é apenas o pequeno tempo de escrever as validações de tipo, o que economiza horas de debug depois.
3. Qual a diferença entre usar unknown e fazer um Type Assertion (as)?
O unknown obriga você a provar o tipo através de lógica (como typeof ou if). Já o Type Assertion (valor as Endereco) é você “forçando a barra” e dizendo ao compilador: “Trate isso como Endereco”. O assertion é mais arriscado, pois se o dado vier errado da API, o TypeScript não vai te avisar e o erro vai estourar no console da pessoa usuária.
4. Posso usar unknown para tipas o JSON.parse()?
Sim, e essa é uma excelente prática. Por padrão, o JSON.parse() retorna any, o que é um perigo silencioso. Ao atribuir o resultado a uma variável unknown, você se força a validar o JSON antes de acessar propriedades que podem não existir, tornando o processamento de dados muito mais robusto.





