Quem estava nas minhas lives lá da Formação TypeScript (ou já fez o treinamento) sabe que eu sou extremamente contra any no código. Principalmente porque o any se tornou uma válvula de escape, sempre que queremos desligar o TypeScript podemos só usar any no nosso código e tudo se resolve. Muita gente já me perguntou: "Ué, então por que o TypeScript tem any se não pode usar?"

A realidade é que o any é um tipo extremamente importante. Primeiro porque ele é o único tipo que pode ser associado a qualquer tipo sem que esse tipo seja restringido a um tipo mais específico, além disso ele é o tipo mais aberto de todos e aceita todas as coisas, ou seja, o any está em todo lugar.

Enquanto na maioria dos casos é muito ruim usar any, existem alguns casos interessantes onde, na verdade, o any é a nossa única opção correta.

Permitindo inferência de tipos

O primeiro exemplo é justamente o que eu comentei ali em cima. Quando usamos any, não estamos fixando um tipo específico e estamos permitindo que o TS faça a inferência desse tipo. O jeito que o TypeScript resolve tipagens é sempre da mais aberta para a mais fechada, ou seja, o any é como inicializar uma variável numérica como "infinito".

Um exemplo clássico disso (que você vai encontrar em muitos lugares) é o ReturnType, um utility type que existe no TypeScript nativamente e basicamente o que ele faz é pegar o tipo do retorno de uma função. Vamos tentar recriar esse tipo:

type ReturnType<T extends (...args: unknown[]) => unknown> = T extends (...args: unknown[]) => infer Retorno ? Retorno : never

Vem aprender comigo!

Quer aprender mais sobre criptografia e boas práticas com #TypeScript?

Se inscreva na Formação TS!

Basicamente, estamos dizendo que queremos um tipo, esse tipo leva um genérico que vai ser uma função, a gente realmente não se importa com o que existe nos parâmetros ou no retorno da função, então para não ter que usar any vamos usar unknown. Dessa forma nosso tipo fica mais seguro, certo? Mas e se a gente fizer isso aqui:

const foo = (i: string) => i
type retorno = ReturnType<typeof foo>

Vamos ter um erro interessante:

ype '(i: string) => string' does not satisfy the constraint '(...args: unknown[]) => unknown'.
  Types of parameters 'i' and 'args' are incompatible.
    Type 'unknown' is not assignable to type 'string'.

Isso porque quando usamos unknown estamos automaticamente dizendo para o TypeScript que a gente não sabe o que tem ali, então o TS vai forçar que a gente faça um casting disso manualmente, o que a gente quer é que o TS faça a inferência sozinho. Então para isso temos que dizer que "não nos importamos com o que está ali" e o TS vai sempre tentar trazer o tipo mais específico possível.

Se mudarmos a nossa declaração de tipo para:

type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer Retorno ? Retorno : never

Nosso erro some e o nosso tipo retorno vai ser string.

Valores externos

Uma outra opção é quando estamos tratando com valores que são verdadeiramente externos. Esse é um valor válido para usar any, mas sempre temos que lembrar que é necessário que a gente faça a tipagem desses tipos depois. Por exemplo:

const dadoExterno: any = algumaChamadaDeAPI()
// processamento aqui
const dadoInterno: SeuTipo = dadoConfirmado

O uso do any nesse caso é somente quando a gente está obtendo o dado, através de um JSON.parse ou qualquer outra chamada, mas é extremamente importante que a gente não mantenha o any depois, se tivermos que fazer qualquer processamento, é importante que a gente faça o casting desse tipo para outro tipo bem mais específico.

Migração de base

Um dos casos de uso mais importantes é quando estamos fazendo a migração de JavaScript para TypeScript em uma base de código antiga. Para isso é comum que a gente comece fazendo a migração usando any no código antigo e, aos poucos, vai passando esses any para tipos específicos.

Esse é provavelmente o caso mais ok para o uso de any em qualquer aplicação.