Analisi statica

L'analisi statica è un tipo di test che consente il controllo automatico del codice senza eseguirlo effettivamente o dover scrivere un test automatico. Probabilmente hai già visto questo tipo di test se utilizzi un IDE come VSCode: il controllo dei tipi eseguito da TypeScript è una sorta di analisi statica che può apparire come linee ondulate sotto gli errori o gli avvisi.

ESLint

ESLint è uno strumento che può fornire feedback su possibili problemi nel codebase. Questi problemi possono essere sicuri,ma anche errori o comportamenti non standard. ESLint consente di applicare una serie di regole controllate sul codebase, incluse molte nel relativo set di regole "consigliate".

Un buon esempio di regola ESLint è la regola no-unsafe-finally. Questo ti impedisce di scrivere istruzioni che modificano il flusso di controllo del tuo programma all'interno di un blocco finally. Questa è un'ottima regola, perché è un modo insolito di scrivere JavaScript che può essere difficile da seguire. Tuttavia, è un aspetto che dovrebbe essere in grado di rilevare anche un processo integro di revisione del codice.

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

Di conseguenza, ESLint non sostituisce un processo di revisione integro (e una guida di stile che definisce l'aspetto del tuo codebase), perché non è in grado di acquisire ogni approccio non ortodosso che uno sviluppatore potrebbe provare a introdurre nel tuo codebase. La guida alle pratiche tecniche di Google contiene una breve sezione su come semplificare la procedura.

ESLint consente di infrangere una regola e annotare il codice come "consentito". Ad esempio, puoi consentire la logica precedente annotandola come segue:

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

Se ti capita di violare costantemente una regola, prova a disattivarla. Questi strumenti ti incoraggiano a scrivere codice in un determinato modo, ma il tuo team potrebbe essere abituato a scrivere codice in un modo diverso ed essere già consapevole dei rischi associati a questo approccio.

Infine, abilitare strumenti di analisi statica su un codebase di grandi dimensioni potrebbe creare molto rumore inutile (e lavoro impegnativo per il refactoring) su codice che altrimenti funzionava bene. In questo modo è più facile abilitarlo nelle prime fasi del ciclo di vita di un progetto.

Plug-in ESLint per il supporto del browser

Puoi aggiungere a ESLint un plug-in che segnala l'uso di API non ampiamente supportate o non supportate dall'elenco dei browser di destinazione. Il pacchetto eslint-plugin-compat può avvisarti quando un'API potrebbe non essere disponibile per i tuoi utenti, in modo da non doverti tenere costantemente traccia.

Controllo del tipo per l'analisi statica

Durante l'apprendimento di JavaScript, ai nuovi sviluppatori viene in genere presentata l'idea che si tratta di un linguaggio digitato in modo debole. In altre parole, puoi dichiarare una variabile come un tipo e utilizzare la stessa posizione per qualcosa di completamente diverso. È simile a Python e ad altri linguaggi di scripting, ma a differenza di linguaggi compilati come C/C++ e Rust.

Questo tipo di linguaggio potrebbe essere utile per iniziare, ed è probabilmente questa semplicità che ha reso JavaScript così popolare, ma spesso è un punto di errore per alcuni codebase o almeno qualcosa che consente errori confusi. Ad esempio, passando un valore number in cui era previsto un string o un tipo di oggetto, il valore digitato in modo errato può propagarsi in varie librerie prima di causare un errore TypeError.

TypeScript

TypeScript è la soluzione più tradizionale per la mancanza di informazioni di digitazione in JavaScript. Questo corso ne fa un uso intensivo. Anche se questo non è un corso su TypeScript, può essere una parte importante del tuo programma perché fornisce un'analisi statica.

Per un rapido esempio, questo codice, che prevede di ricevere un callback che accetta un nome string e un'età number:

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

Genera il seguente errore quando viene eseguito tramite TypeScript o anche quando ci si passa il mouse sopra in 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
Il codice dell'esempio precedente, visualizzato in un IDE con il messaggio di errore visualizzato in un popup.
VSCode indica che hai trasmesso un tipo errato.

Essenzialmente, l'obiettivo dell'uso di TypeScript è evitare errori di questo tipo (l'età dovrebbe essere un number, non un string) che compaiono nel tuo progetto. Questo tipo di errore può essere difficile da rilevare utilizzando altri tipi di test. Inoltre, il sistema dei tipi può fornire feedback prima ancora che venga scritto un test. Questo può semplificare il processo di scrittura del codice fornendoti feedback tempestivi sugli errori di tipo durante lo sviluppo del software, anziché quando il codice viene eseguito alla fine.

La parte più difficile dell'uso di TypeScript è la sua corretta configurazione. Ogni progetto ha bisogno di un file tsconfig.json che, sebbene utilizzato principalmente dallo strumento a riga di comando tsc stesso, viene letto anche dagli IDE come VSCode insieme a molti altri strumenti e strumenti di compilazione, tra cui Vitest. Questo file contiene centinaia di opzioni e flag. Qui puoi trovare alcune risorse utili per configurarlo:

Suggerimenti generali per TypeScript

Quando configuri e utilizzi TypeScript tramite un file tsconfig.json, tieni presente quanto segue:

  • Assicurati che i file di origine siano effettivamente inclusi e controllati. Se misteriosamente un file "non contiene errori", è probabile che non sia stato controllato.
  • La descrizione esplicita di tipi e interfacce all'interno dei file .d.ts, anziché essere descritti implicitamente durante la scrittura delle funzioni, può semplificare i test del tuo codebase. Quando le interfacce coinvolte sono chiare, è più facile scrivere versioni fittizie e "false". .

TypeScript implicito qualsiasi

Una delle opzioni di configurazione più potenti e gratificanti di TypeScript è il flag noImplicitAny. Tuttavia, è spesso anche la più difficile da abilitare, soprattutto se hai già un codebase di grandi dimensioni. Il flag noImplicitAny è abilitato per impostazione predefinita se sei in modalità strict, ma non negli altri casi.

Con questo flag la funzione restituirà un errore:

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

Anche se, come lettore, è abbastanza chiaro che n deve essere un numero, TypeScript non è in grado di confermarlo con certezza. Se utilizzi VSCode, passando il mouse sopra la funzione, la descrizione verrà descritta come segue:

function fibonacci(n: any): any

I chiamanti di questa funzione potranno passare attraverso un valore di tipo any (un tipo che consente qualsiasi altro tipo), non solo un number. Se attivi il flag noImplicitAny, puoi salvaguardare questo tipo di codice durante lo sviluppo, senza dover scrivere test approfonditi della logica di business per il codice che trasmette i tipi di dati sbagliati in luoghi specifici.

La soluzione più semplice è contrassegnare sia l'argomento n sia il tipo restituito di fibonacci come number.

Il flag noImplicitAny non ti impedisce di scrivere esplicitamente any nel tuo codebase. Puoi comunque scrivere una funzione che accetti o restituisca il tipo any. Garantisce solo di assegnare un tipo a ogni variabile.