Análise estática

A análise estática é um tipo de teste que fornece verificação automatizada do código sem realmente executá-lo ou ter que escrever um teste automatizado. Você provavelmente já viu esse tipo de teste ao usar um ambiente de desenvolvimento integrado como o VSCode. A verificação de tipo realizada pelo TypeScript é um tipo de análise estática e pode aparecer como linhas curvas em erros ou avisos.

ESLint

O ESLint é uma ferramenta que pode fornecer feedback sobre possíveis problemas na sua base de código. Esses problemas podem ser de tipo seguro,mas são erros ou comportamentos fora do padrão. O ESLint permite aplicar várias regras que são verificadas na base de código, incluindo muitas no conjunto "recomendado".

Um bom exemplo de regra ESLint é a no-unsafe-finally (link em inglês). Isso evita que você escreva instruções que modifiquem o fluxo de controle do programa dentro de um bloco finally. Essa é uma ótima regra, porque fazer isso é uma maneira incomum de escrever em JavaScript, que pode ser difícil de seguir. No entanto, isso também é algo que um processo de revisão de código íntegro é capaz de detectar.

  try {
    const result = await complexFetchFromNetwork();
    if (!result.ok) {
      throw new Error("failed to fetch");
    }
  } finally {
    // warning - this will 'overrule' the previous exception!
    return false;
  }

Dessa forma, o ESLint não substitui um processo de revisão saudável (e um guia de estilo que define a aparência da base de código), já que ela não vai capturar todas as abordagens não ortodoxas que um desenvolvedor pode tentar introduzir na sua base de código. O guia de práticas de engenharia do Google tem uma pequena seção sobre como simplificar as coisas.

O ESLint permite violar uma regra e anotar o código como "permitido". Por exemplo, você pode permitir a lógica anterior fazendo uma anotação desta forma:

  finally {
    // eslint-disable-next-line no-unsafe-finally
    return false;
  }

Se você perceber que está quebrando uma regra constantemente, considere desativá-la. Essas ferramentas incentivam você a escrever código de uma determinada maneira, mas sua equipe pode estar acostumada a escrever código de outra maneira e já estar ciente dos riscos dessa abordagem.

Por fim, ativar ferramentas de análise estática em uma grande base de código pode criar muito ruído inútil (e trabalho de refatoração) sobre o código que, de outra forma, funcionava bem. Por isso, é mais fácil ativar no início do ciclo de vida do projeto.

Plug-ins ESLint para suporte a navegadores

Você pode adicionar um plug-in ao ESLint que sinaliza o uso de APIs que não têm suporte ampla ou não são compatíveis com a lista de navegadores de destino. O pacote eslint-plugin-compat pode avisar quando uma API pode não estar disponível para os usuários, para que você não precise monitorar constantemente.

Verificação de tipo para análise estática

Ao aprender sobre JavaScript, os novos desenvolvedores geralmente são apresentados à ideia de que se trata de uma linguagem com digitação fraca. Ou seja, é possível declarar uma variável como um tipo e usar o mesmo local para algo completamente diferente. Ele é parecido com o Python e outras linguagens de script, mas diferente das linguagens compiladas, como C/C++ e Rust.

Esse tipo de linguagem pode ser bom para começar, e é sem dúvida essa simplicidade que tornou o JavaScript tão popular, mas muitas vezes é um ponto de falha para algumas bases de código ou, pelo menos, algo que permite a ocorrência de erros confusos. Por exemplo, ao transmitir um number em que um string ou um tipo de objeto era esperado, esse valor digitado incorretamente pode ser propagado por várias bibliotecas antes de finalmente causar uma TypeError confusa.

TypeScript

O TypeScript é a solução mais comum para a falta de informações de digitação do JavaScript. Este curso o usa extensivamente. Embora não seja um curso sobre TypeScript, ele pode ser uma parte importante da sua caixa de ferramentas, porque fornece análise estática.

Para conferir um exemplo rápido, este código, que espera receber um callback que aceita um nome de string e uma idade de number:

const callback = (name: string, age: string): void => {
  console.info(name, 'is now', age, 'years old!');
};
onBirthday(callback);

Gera o seguinte erro quando executado pelo TypeScript ou mesmo ao passar o cursor em um ambiente de desenvolvimento integrado:

bad.ts:4:12 - error TS2345: Argument of type '(name: string, age: string) => void' is not assignable to parameter of type '(name: string, age: number) => void'.
  Types of parameters 'age' and 'age' are incompatible.
    Type 'number' is not assignable to type 'string'.

4 onBirthday(callback);
             ~~~~~~~~

Found 1 error in bad.ts:4
O código do
  exemplo anterior, exibido em um ambiente de desenvolvimento integrado com a mensagem de erro em um
  pop-up.
VSCode indicando que você transmitiu um tipo incorreto.

Por fim, o objetivo de usar o TypeScript é evitar erros como esse (a idade precisa ser um number, não um string) no projeto. Esse tipo de erro pode ser difícil de detectar usando outros tipos de teste. Além disso, o sistema de tipos pode dar feedback antes mesmo de um teste ser criado. Isso pode facilitar o processo de escrever código, oferecendo feedback antecipado sobre erros de tipo enquanto você desenvolve o software, e não quando o código é executado.

A parte mais desafiadora de usar o TypeScript é configurá-lo corretamente. Todo projeto precisa de um arquivo tsconfig.json, que, embora usado principalmente pela própria ferramenta de linha de comando tsc, também é lido por ambientes de desenvolvimento integrado como o VSCode, além de muitas outras ferramentas de build e ferramentas, incluindo o Vitest. Esse arquivo contém centenas de opções e sinalizações, e você pode encontrar alguns bons recursos para configurá-lo aqui:

Dicas gerais do TypeScript

Ao configurar e usar o TypeScript com um arquivo tsconfig.json, lembre-se do seguinte:

  • Confirme se os arquivos de origem foram incluídos e verificados. Se um arquivo "não contém erros" misteriosamente, é provável que ele não esteja sendo verificado.
  • Descrever explicitamente tipos e interfaces em arquivos .d.ts, em vez de tê-los descrito implicitamente enquanto você escreve funções, pode facilitar o teste da base de código. É mais fácil escrever simulações e versões "falsas" de código quando as interfaces envolvidas estão claras. .

TypeScript implícito qualquer

Uma das opções de configuração mais eficientes e gratificantes do TypeScript é a flag noImplicitAny. No entanto, geralmente ela é a mais difícil de ativar, especialmente se você já tem uma grande base de código. A sinalização noImplicitAny será ativada por padrão se você estiver no modo strict.

Esta sinalização fará com que a função retorne um erro:

export function fibonacci(n) {
  if (n <= 1) {
    return 0;
  } else if (n === 2) {
    return 1;
  }
  return fibonacci(n - 1) + fibonacci(n - 2);
}

Embora, como leitor, esteja bastante claro que n precisa ser um número, o TypeScript não pode confirmar isso com confiança. Se você estiver usando o VSCode, passe o cursor sobre a função da seguinte maneira:

function fibonacci(n: any): any

Os autores da chamada dessa função poderão transmitir por um valor do tipo any (um tipo que permite qualquer outro tipo), não apenas um number. Ao ativar a flag noImplicitAny, é possível proteger esse tipo de código durante o desenvolvimento, sem precisar programar extensos testes de lógica de negócios para que o código transmita os tipos de dados errados em locais específicos.

A correção simples aqui é marcar o argumento n e o tipo de retorno de fibonacci como number.

A sinalização noImplicitAny não impede que você escreva any explicitamente na sua base de código. Você ainda pode criar uma função que aceite ou retorne o tipo any. Isso apenas garante um tipo para cada variável.