Análisis estático

El análisis estático es un tipo de prueba que proporciona una verificación automatizada de tu código sin ejecutarlo ni tener que escribir una prueba automatizada. Es probable que ya hayas visto este tipo de pruebas si usas un IDE como VSCode; la verificación de tipo que realiza TypeScript es una especie de análisis estático, y puede aparecer como líneas onduladas debajo de errores o advertencias.

ESLint

ESLint es una herramienta que puede proporcionar comentarios sobre posibles problemas en tu base de código. Estos problemas pueden ser de tipo seguro,pero también pueden ser errores o comportamientos no estándar en sí mismos. ESLint te permite aplicar una serie de reglas que se verifican en tu base de código, incluidas muchas en su conjunto "recomendado".

Un buen ejemplo de una regla ESLint es su regla no-unsafe-finally. De esta manera, se evita que escribas sentencias que modifiquen el flujo de control de tu programa dentro de un bloque finally. Esta es una regla muy útil, ya que es una forma inusual de escribir JavaScript que puede ser difícil de seguir. Sin embargo, también es algo que un proceso de revisión de código en buen estado debería poder 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;
  }

Por lo tanto, ESLint no reemplaza un proceso de revisión en buen estado (ni una guía de estilo que define cómo debería verse tu base de código), ya que no capturará todos los enfoques poco ortodoxos que un desarrollador podría intentar introducir en tu base de código. La guía de prácticas de ingeniería de Google tiene una sección breve sobre cómo simplificarla.

ESLint te permite romper una regla y anotar el código como "permitido". Por ejemplo, puedes permitir la lógica anterior si la anotas de la siguiente manera:

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

Si constantemente rompes una regla, considera desactivarla. Estas herramientas te alientan a escribir código de cierta manera, pero es posible que tu equipo esté acostumbrado a escribir código de otra manera y ya esté al tanto de los riesgos de ese enfoque.

Por último, habilitar herramientas de análisis estáticas en una base de código grande podría crear mucho ruido inútil (y trabajo para refactorizar) sobre el código que, de lo contrario, funcionaría bien. Por lo tanto, es más fácil habilitar al principio del ciclo de vida de un proyecto.

Complementos de ESLint para la compatibilidad con navegadores

Puedes agregar un complemento a ESLint que marque el uso de las APIs que no son ampliamente compatibles o no son compatibles con tu lista de navegadores de destino. El paquete eslint-plugin-compat puede advertirte cuando una API no esté disponible para tus usuarios, de modo que no tengas que realizar un seguimiento constante.

Verificación de tipos para análisis estático

Cuando los desarrolladores nuevos aprenden JavaScript, por lo general, se les presenta la idea de que es un lenguaje de escritura débil. Es decir, es posible declarar una variable como un tipo y, luego, usar la misma ubicación para algo completamente diferente. Esto es similar a Python y otros lenguajes de programación, pero a diferencia de los lenguajes compilados, como C/C++ y Rust.

Este tipo de lenguaje puede ser bueno para comenzar, y se podría decir que esa simplicidad hizo que JavaScript se volviera tan popular, pero a menudo es un punto de falla para algunas bases de código o, al menos, algo que permite que ocurran errores confusos. Por ejemplo, si pasas un number en el que se esperaba una string o un tipo de objeto, ese valor escrito de forma incorrecta puede propagarse a través de varias bibliotecas antes de causar, finalmente, una TypeError confusa.

TypeScript

TypeScript es la solución más común para la falta de información de escritura en JavaScript. En este curso, se usa ampliamente. Aunque este no es un curso sobre TypeScript, puede ser una parte importante de tu caja de herramientas porque proporciona análisis estático.

Para ver un ejemplo rápido, este código, que espera recibir una devolución de llamada que acepte un nombre string y una edad number:

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

Genera el siguiente error cuando se ejecuta con TypeScript o incluso cuando se coloca el cursor sobre un IDE:

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
El código del ejemplo anterior, en un IDE y con el mensaje de error en una ventana emergente.
VSCode indica que pasaste un tipo incorrecto.

En última instancia, el objetivo de usar TypeScript es evitar errores como este (la edad debe ser una number, no una string) que ingresen en tu proyecto. Este tipo de error puede ser difícil de detectar con otros tipos de pruebas. Además, el sistema de tipos puede proporcionar comentarios incluso antes de que se escriba una prueba. Esto puede facilitar el proceso de escritura de código, ya que te brinda comentarios anticipados sobre los errores de tipo mientras desarrollas el software, en lugar de hacerlo cuando el código finalmente se ejecuta.

La parte más difícil de usar TypeScript es configurarla correctamente. Cada proyecto necesita un archivo tsconfig.json que, aunque lo usa principalmente la herramienta de línea de comandos tsc, también lo leen los IDE como VSCode junto con muchas otras herramientas y herramientas de compilación, incluido Vitest. Este archivo contiene cientos de opciones y marcas. Aquí puedes encontrar algunos recursos útiles para configurarlo:

Sugerencias generales de TypeScript

Cuando configures y uses TypeScript a través de un archivo tsconfig.json, ten en cuenta lo siguiente:

  • Asegúrate de que los archivos fuente estén incluidos y verificados. Si un archivo “no tiene errores” de forma misteriosa, es probable que se deba a que no se está verificando.
  • La descripción explícita de los tipos y las interfaces dentro de los archivos .d.ts, en lugar de que se describan implícitamente mientras escribes funciones, puede facilitar la prueba de tu base de código. Es más fácil escribir simulaciones y versiones “falsas” de código cuando las interfaces involucradas son claras. .

TypeScript implícito

Una de las opciones de configuración más potentes y gratificantes de TypeScript es la marca noImplicitAny. Sin embargo, también suele ser la más difícil de habilitar, en especial si ya tienes una base de código grande. (La marca noImplicitAny está habilitada de forma predeterminada si estás en modo strict, pero no en otro caso).

Esta marca hará que la función muestre un error:

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

Aunque, como lector, está bastante claro que n debe ser un número, TypeScript no puede confirmarlo con seguridad. Si usas VSCode, coloca el cursor sobre la función y se describirá de la siguiente manera:

function fibonacci(n: any): any

Los llamadores de esta función podrán pasar un valor de tipo any (un tipo que permite cualquier otro tipo), no solo un number. Si habilitas la marca noImplicitAny, puedes proteger este tipo de código durante el desarrollo sin necesidad de escribir pruebas exhaustivas de lógica empresarial para tu código pasando los tipos de datos incorrectos en lugares específicos.

La solución simple aquí es marcar el argumento n y el tipo de datos que se muestra de fibonacci como number.

La marca noImplicitAny no te impide escribir any de forma explícita en tu base de código. Aún puedes escribir una función que acepte o muestre el tipo any. Solo garantiza que se le asigne un tipo a cada variable.