Analyse statique

L'analyse statique est un type de test qui permet de vérifier automatiquement votre code sans l'exécuter réellement ni avoir à écrire un test automatisé. Vous avez probablement déjà vu ce type de test si vous utilisez un IDE tel que VSCode. La vérification du type effectuée par TypeScript est une sorte d'analyse statique qui peut apparaître sous forme de lignes ondulées sous les erreurs ou les avertissements.

ESLint

ESLint est un outil qui peut fournir des commentaires sur les problèmes éventuels dans votre codebase. Ces problèmes peuvent être sûrs au typage,mais des erreurs ou un comportement non standard sont de leur côté. ESLint vous permet d'appliquer un certain nombre de règles vérifiées sur votre codebase, y compris de nombreuses règles dans son ensemble "recommandé".

La règle no-unsafe-finally est un bon exemple de règle ESLint. Cela vous empêche d'écrire des instructions qui modifient le flux de contrôle de votre programme dans un bloc finally. C'est une excellente règle, car cela représente une manière inhabituelle d'écrire du code JavaScript qui peut être difficile à suivre. Cependant, c'est également quelque chose qu'un processus de révision de code sain doit pouvoir détecter.

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

Ainsi, ESLint ne remplace pas un processus d'examen opérationnel (et un guide de style qui définit à quoi doit ressembler votre codebase), car il ne capture pas toutes les approches peu conventionnelles qu'un développeur pourrait tenter d'introduire dans votre codebase. Le guide des pratiques d'ingénierie de Google contient une courte section sur la simplicité.

ESLint vous permet d'enfreindre une règle et d'annoter le code comme "autorisé". Par exemple, vous pouvez autoriser la logique précédente en l'annotant comme suit:

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

Si vous enfreignez constamment une règle, envisagez de la désactiver. Ces outils vous encouragent à écrire du code d'une certaine manière, mais votre équipe peut être habituée à écrire du code d'une manière différente et être déjà consciente des risques liés à cette approche.

Enfin, l'activation des outils d'analyse statique sur un codebase volumineux peut créer beaucoup de bruit inutile (et une charge de travail de refactorisation) par rapport au code qui fonctionnait autrement. Il est donc plus facile de l'activer au début du cycle de vie d'un projet.

Plug-ins ESLint pour la compatibilité avec les navigateurs

Vous pouvez ajouter à ESLint un plug-in qui signale l'utilisation d'API peu compatibles ou non compatibles avec votre liste de navigateurs cibles. Le package eslint-plugin-compat peut vous avertir lorsqu'une API risque de ne pas être disponible pour vos utilisateurs. Vous n'avez donc pas besoin d'effectuer un suivi constant par vous-même.

Vérification du type pour l'analyse statique

Lorsqu'ils apprennent en JavaScript, les nouveaux développeurs découvrent généralement qu'il s'agit d'un langage à faible typage. Autrement dit, il est possible de déclarer une variable en tant que type, puis d'utiliser le même emplacement pour un autre élément complètement différent. Ce langage est semblable à Python et à d'autres langages de script, mais contrairement aux langages compilés tels que C/C++ et Rust.

Ce type de langage peut être utile pour démarrer, et c'est sans doute cette simplicité qui a rendu JavaScript si populaire. Toutefois, c'est souvent un point de défaillance pour certains codebases, ou du moins quelque chose qui permet des erreurs prêtant à confusion. Par exemple, en transmettant une valeur number dans laquelle un string ou un type d'objet était attendu, cette valeur mal saisie peut se propager dans différentes bibliothèques avant de générer une TypeError déroutante.

TypeScript

TypeScript est la solution la plus répandue face au manque d'informations de saisie de JavaScript. Ce cours l'utilise abondamment. Bien qu'il ne s'agisse pas d'un cours sur TypeScript, il peut constituer une partie importante de votre boîte à outils car il fournit une analyse statique.

Voici un exemple rapide, qui s'attend à recevoir un rappel acceptant un nom string et un âge number:

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

Génère l'erreur suivante lors de l'exécution via TypeScript, ou même lorsque l'utilisateur pointe dessus dans 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
Code de l'exemple précédent, affiché dans un IDE avec le message d'erreur dans un pop-up.
VSCode indiquant que vous avez transmis un type incorrect

En fin de compte, l'utilisation de TypeScript vise à éviter que de telles erreurs de ce type (l'âge doit être un number et non un string) ne se propagent dans votre projet. Ce type d'erreur peut être difficile à détecter à l'aide d'autres types de tests. De plus, le système de types peut fournir des commentaires avant même la rédaction d'un test. Cela peut faciliter le processus d'écriture de code en vous envoyant des commentaires précoces sur les erreurs de type lors du développement du logiciel, plutôt que lorsqu'il s'exécute à terme.

La partie la plus difficile de l'utilisation de TypeScript est de le configurer correctement. Chaque projet nécessite un fichier tsconfig.json, qui, bien qu'il soit principalement utilisé par l'outil de ligne de commande tsc lui-même, est également lu par les IDE tels que VSCode ainsi que par de nombreux autres outils et outils de compilation, y compris Vitest. Ce fichier contient des centaines d'options et d'indicateurs. Vous trouverez ci-dessous des ressources utiles pour le configurer:

Conseils généraux sur TypeScript

Lorsque vous configurez et utilisez TypeScript via un fichier tsconfig.json, tenez compte des points suivants:

  • Assurez-vous que vos fichiers sources sont bien inclus et cochés. Si un fichier "ne contient aucune erreur", c'est probablement parce qu'il n'est pas vérifié.
  • Décrire explicitement les types et les interfaces dans les fichiers .d.ts, au lieu de les avoir implicitement décrits lorsque vous écrivez des fonctions, peut faciliter les tests de votre codebase. Il est plus facile d'écrire des simulations et des versions "falsifiées" du code lorsque les interfaces impliquées sont claires. .

TypeScript implicite n'importe lequel

L'une des options de configuration les plus puissantes et les plus gratifiantes de TypeScript est l'indicateur noImplicitAny. Toutefois, il est souvent aussi le plus difficile à activer, en particulier si vous disposez déjà d'un codebase volumineux. (L'option noImplicitAny est activée par défaut si vous êtes en mode strict, mais pas autrement.)

Cette option obligera cette fonction à renvoyer une erreur:

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

Même si, en tant que lecteur, il est assez clair que n doit être un nombre, TypeScript ne peut pas le confirmer avec certitude. Si vous utilisez VSCode, passez la souris sur la fonction pour la décrire comme suit:

function fibonacci(n: any): any

Les appelants de cette fonction pourront transmettre une valeur de type any (un type qui autorise tout autre type), et pas seulement number. En activant l'option noImplicitAny, vous pouvez protéger ce type de code pendant le développement, sans avoir à écrire de tests de logique métier approfondis pour que votre code transmette les mauvais types de données à des endroits spécifiques.

La solution la plus simple consiste à marquer l'argument n et le type renvoyé de fibonacci sur number.

L'option noImplicitAny ne vous empêche pas d'écrire explicitement any dans votre codebase. Vous pouvez toujours écrire une fonction qui accepte ou renvoie le type any. Cela garantit simplement que vous attribuez un type à chaque variable.