TypeScript: quando usar unknown em vez de any?

mikedesousa80 
mikedesousa80 

Compartilhe

Avalie este artigo

13 minutos de leitura

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: 

  1. 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. 
  1. Pressa no desenvolvimento: Aquele famoso “depois eu arrumo” que acaba ficando para sempre no repositório. 
  1. 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.”

É 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. 

Banner de aniversário da Alura com mensagem sobre evolução de carreira em tecnologia. A imagem destaca a oportunidade de estudar e crescer profissionalmente com cursos online, com botão “Aproveite” para acessar a plataforma e desenvolver habilidades tech.

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 é. 

Captura do VS Code com código TypeScript: função imprimir(msg: string) e variável dado: unknown = "Oi". Ao chamar imprimir(dado), aparece erro TS2345 informando que argumento do tipo unknown não é atribuível ao parâmetro do tipo string.

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: 

  1. Primeiro testamos se o valor é um array 
  1. 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? 
  1. A interface como contrato: Criamos a interface EnderecoViaCEP para definir como esperamos que os dados sejam. É o nosso “cenário ideal”. 
  1. 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. 
  1. 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”. 
  1. 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. 

Avalie este artigo

Veja outros artigos sobre Programação