Menu

any vs unknown em TypeScript: diferenças e quando usar

any e unknown aceitam qualquer valor. any desliga a verificação de tipos para aquele valor, enquanto unknown obriga você a verificar o valor antes de usá-lo. Veja as diferenças, como fazer narrowing de unknown, o noImplicitAny e por onde o any entra em código tipado.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

any e unknown aceitam qualquer valor. A diferença está no que você pode fazer com o valor depois: any deixa fazer qualquer coisa e não verifica nada, enquanto unknown não deixa fazer quase nada até você provar o que o valor é.

Sem a linha @ts-expect-error, u.toUpperCase() é o erro de compilação TS18046. A verificação com typeof estreita u para string, e dentro desse bloco todos os métodos de string ficam disponíveis.

any vs unknown em resumo

anyunknown
Aceita qualquer valorsimsim
Atribuir a um string, number, ...sim, sem verificaçãonão (TS2322)
Ler uma propriedade, chamar um métodosim, sem verificaçãonão (TS18046)
Chamar como funçãosimnão
Aritmética e comparação (x * 2, x + 1, x < 5)simnão (TS18046)
Precisa de verificação antes de usarnãosim (typeof, instanceof, in, um type guard)
Efeito na verificação de tiposdesligada para aquele valor e tudo o que ele tocamantida

Em termos de teoria de tipos, unknown é o top type: todo tipo pode ser atribuído a ele, e ele só pode ser atribuído a unknown e any. O any é uma válvula de escape atribuível nos dois sentidos, a tudo exceto never.

any desliga a verificação de tipos

Um valor com tipo any recebe confiança cega. O compilador aceita erros de digitação, tipos errados e propriedades que faltam, e os erros aparecem em tempo de execução.

A saída mostra o problema: uma variável anotada como number guarda uma string, e a última linha lança Cannot read properties of undefined (reading 'city'). O any também se espalha. user.name é any, então qualquer valor calculado a partir dele é any, e um único valor sem tipo pode desligar a verificação longe de onde entrou.

unknown obriga a verificar antes

Com unknown, o compilador recusa toda operação até o código fazer o narrowing do valor. O narrowing usa verificações comuns de JavaScript, e dentro de cada ramo o valor tem o tipo verificado.

A verificação value === null precisa vir antes da verificação de objeto porque typeof null é "object". Depois de "id" in value, o TypeScript sabe que o objeto tem uma propriedade id, com tipo unknown, já que nada diz o que ela guarda. String() a transforma em texto de forma explícita; para usá-la como número, você a verificaria antes com typeof.

Você também pode pular a verificação com uma assertion, value as string, e o compilador aceita. Isso é uma promessa sem nenhuma verificação em tempo de execução por trás, então prefira uma verificação de verdade; veja type assertions.

Validando JSON com unknown

JSON.parse é declarado como retornando any, então o resultado dele desliga a verificação em silêncio. Anote o resultado como unknown e escreva um type guard que verifica o formato antes que o resto do código confie nele.

A primeira entrada imprime dark at 14px, a segunda é rejeitada porque falta fontSize. Com any, a segunda entrada teria passado como um Settings com fontSize undefined. Para schemas grandes, uma biblioteca de validação faz o mesmo trabalho e deriva o tipo para você.

noImplicitAny

O any não vem só de alguém escrevê-lo. Um parâmetro sem anotação e sem contexto para inferir também seria any. A opção noImplicitAny, que o strict liga, aponta isso como erro:

index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.

A correção é uma anotação, function double(x: number). Escrever x: any explicitamente também compila, e é essa a ideia: o any fica visível no código e pode ser procurado e revisado.

Por onde o any ainda entra

Mesmo com strict, estes casos produzem any sem que a palavra apareça no seu código:

OrigemO que você recebeO que fazer
JSON.parse(text)anyanotar como unknown e validar
response.json() com os tipos do navegador (DOM)Promise<any>o mesmo que no JSON.parse (os tipos do fetch do próprio Node já retornam Promise<unknown>)
Um pacote sem definições de tipoany nos imports (com erro, a menos que você adicione uma declaração)instalar @types/... ou escrever um .d.ts
value as anyanyusar um type guard ou uma assertion precisa
catch (e) com useUnknownInCatchVariables desligadoanyo strict o torna unknown; mantenha assim

A variável do catch é unknown com strict porque qualquer coisa pode ser lançada, não só objetos Error. Faça o narrowing com e instanceof Error antes de ler e.message.

Quando o any é aceitável

O any não é proibido, mas cada um é um ponto que o compilador deixa de proteger. Usos razoáveis:

  • Migrar uma base de código JavaScript, em que o any marca o que ainda não foi tipado.
  • Código que o sistema de tipos não consegue expressar bem, mantido pequeno e atrás de uma assinatura de função tipada.
  • Código de teste que passa dados inválidos de propósito.

Para todo o resto, unknown cobre o mesmo caso de "não sei esse tipo" mantendo as verificações. Muitos times impõem isso com a regra de lint @typescript-eslint/no-explicit-any. Um tipo relacionado, Record<string, unknown>, é a escolha usual para "algum objeto com valores desconhecidos".

Perguntas frequentes

Qual é a diferença entre any e unknown no TypeScript?

Os dois aceitam qualquer valor. Com any você pode fazer qualquer coisa com o valor (ler propriedades, chamá-lo, atribuí-lo a um number) e o compilador não verifica nada. Com unknown você não pode fazer quase nada até fazer o narrowing com uma verificação como typeof x === "string". O unknown mantém a verificação de tipos ligada, e por isso é a escolha mais segura.

Quando usar unknown em vez de any?

Sempre que o tipo de um valor não é conhecido em tempo de compilação: JSON parseado, dados de uma resposta de rede, um erro capturado, a entrada de uma função de validação. Tipe como unknown e faça o narrowing. Use any só em trabalho temporário de migração ou em código que o sistema de tipos não consegue descrever.

O que significa "Object is of type 'unknown'"?

Os erros TS18046 ('x' is of type 'unknown') e TS2571 (Object is of type 'unknown', usado quando o valor não é um nome simples, como load().id) significam que você usou um valor unknown como se ele tivesse um tipo específico, por exemplo lendo uma propriedade ou chamando um método. Verifique o tipo antes (typeof, instanceof, Array.isArray, in ou uma função type guard) e use o valor dentro do ramo com o tipo já estreitado.

Por que JSON.parse retorna any?

A declaração dele na biblioteca padrão diz parse(text: string, ...): any, porque o compilador não tem como saber o que uma string contém. O resultado desliga em silêncio a verificação de tudo o que ele toca. Anote-o: const data: unknown = JSON.parse(text) e valide antes de usar.

O que é noImplicitAny?

Uma opção do compilador, parte do strict, que aponta um erro quando uma declaração receberia em silêncio o tipo any por não ter anotação nem nada de onde inferir: TS7006 para um parâmetro, TS7005 ou TS7034 para uma variável cujo tipo não pode ser deduzido. Ela impede que o any apareça sem ninguém escrevê-lo. Um any explícito continua permitido.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR