Rendere più specifiche le soppressioni degli errori di flusso

Mar 19 2020
Stiamo migliorando le soppressioni degli errori di flusso in modo che non nascondano accidentalmente gli errori. Nel corso del prossimo anno, Flow prevede di apportare molte modifiche e miglioramenti al proprio sistema di tipi.
Foto di icon0.com da Pexels

Stiamo migliorando le soppressioni degli errori di flusso in modo che non nascondano accidentalmente gli errori.

Nel corso del prossimo anno, Flow prevede di apportare molte modifiche e miglioramenti al proprio sistema di tipi . Una delle conseguenze di ciò, tuttavia, è che potrebbe essere necessario aggiungere ulteriori soppressioni al codice man mano che Flow migliora nel trovare i problemi esistenti.

Questo può essere un problema perché le soppressioni di Flow influenzano più del semplice errore originale che si intendeva sopprimere. Ciò riduce la capacità di Flow di rilevare gli errori. Questo è il risultato di due proprietà: in primo luogo, che le soppressioni di flusso possono essere posizionate in qualsiasi parte di un errore di tipo e, in secondo luogo, che le soppressioni di flusso possono sopprimere qualsiasi tipo di errore di tipo . Per correggere queste carenze, richiederemo che le soppressioni vengano inserite nel punto del codice in cui si verifica effettivamente l'errore e che includano un codice di errore che specifichi il tipo di errore che si desidera eliminare.

Per capire cosa significa tutto questo, approfondiamo la natura degli errori di tipo di Flow.

Cos'è un errore di tipo?

Quando Flow incontra un problema durante il controllo del codice, come un'incompatibilità tra due tipi o un accesso a una proprietà non valido, genera un errore che segnala all'utente esattamente cosa non va nel codice. Questi errori possono contenere un'ampia varietà di informazioni a seconda delle circostanze particolari che li hanno causati, ma i due che ci interessano di più sono il tipo di errore e le posizioni dell'errore.

Il tipo di errore codifica la natura specifica del cattivo comportamento che Flow ha rilevato nel codice, sia che si tratti di qualcosa di semplice come passare troppi argomenti a una funzione, o qualcosa di complesso come la diffusione di un tipo di unione. Queste informazioni indicano a Flow esattamente quale tipo di informazioni visualizzare all'utente per spiegare al meglio esattamente dove hai sbagliato.

Le posizioni dell'errore contengono informazioni su dove si trova l'errore nel codice. Esistono due tipi di posizioni che un errore può contenere, una posizione principale (di cui può essercene solo una) e una posizione secondaria , di cui possono essercene molte o nessuna. La posizione primaria corrisponde approssimativamente alla posizione nel codice in cui si è effettivamente verificato l'errore, mentre le posizioni secondarie contengono informazioni sui siti di definizione dei tipi e dei valori coinvolti nell'errore. Per esempio:

function foo(x : number) : void {}
foo("hello");
/* error */
foo("hello");
       ^ Cannot call `foo` with `"hello"` bound to `x` because string [1] is incompatible with number [2].
References:
2: foo("hello");
       ^ [1]
1: function foo(x : number) : void {}
                    ^ [2]

function foo(x : number) : void {}
let y : string = "hello";
foo(y);
/* error */
foo(y);
       ^ Cannot call `foo` with `y` bound to `x` because string [1] is incompatible with number [2].
References:
2: let y : string = "hello";
           ^ [1]
1: function foo(x : number) : void {}
                    ^ [2]

Allora cosa c'è di così sbagliato nelle soppressioni?

Bene, poiché le soppressioni possono essere applicate sia alla posizione primaria che a quella secondaria, inserire un //$FlowFixMecommento sopra la definizione di foosopprimerebbe gli errori in entrambi gli esempi precedenti. Dal momento che fooappare come una posizione secondaria in ogni errore che lo utilizza, ciò significherebbe che qualsiasi utilizzo di footale risultato in un errore sarebbe soppresso da questa soppressione, ovunque possa verificarsi. Saresti in grado di chiamare foocon un numero qualsiasi di argomenti di qualsiasi tipo e Flow non mostrerebbe alcun errore o avvertimento su nulla di ciò. Ciò degrada la fiducia degli utenti nel controllo del tipo di Flow e consente ai bug che altrimenti avrebbe individuato di entrare in produzione.

Oltre a ciò, le soppressioni di Flow influenzano tutti i tipi di errori, purché contengano una posizione coperta dalla soppressione. Ciò significa che più errori di diverso tipo possono essere soppressi da un'unica soppressione. Per vedere il pericolo in questo, considera il seguente esempio:

// library file lib.js
function foo(x : number) : void {}
// impl file
const {foo} = require("lib.js");
let y : string = "hello";
// $FlowFixMe
foo(y);

What are we going to do about this?

To address this we are going to make the following changes:

  • Enforce primary locations. We will be changing the suppression behavior such that suppressions only apply to errors when placed at their primary location. For example, instead of allowing you to suppress an invalid call at the definition of the function, we will now require that the suppression be located at the call-site itself. This way, Flow will still be able to alert you about other possibly invalid calls to the function.
  • Add error codes. We will also be adding error codes to our suppressions so they only suppress errors of the specified kind. This will prevent suppressions from unexpectedly suppressing a kind of error you were not expecting to encounter.
  • Standardize suppression syntax. Previously the acceptable syntax for an error suppression could be configured manually in the .flowconfig for a given project, allowing inconsistent suppression syntax across projects. As part of the above changes we will also be standardizing the suppression syntax; only supporting the $FlowFixMe[incompatible-type] or $FlowExpectedError[incompatible-type] formats.

Rollout

We recognize that this change may cause a significant number of errors in your codebases to become invalid, especially if you were in the habit of placing suppressions in library code. To ease this burden, we have a couple of suggestions:

  • To relocate your newly invalid suppressions to their primary locations, we recommend a combination of the add-comments and remove-comments utilities provided in the Flow tool. Running ./tool remove-comments will remove any comments that no longer suppress an error because they are not on a primary location, and ./tool add-comments will place new suppressions on unsuppressed locations. The ./tool script can be accessed by cloning the Flow repository on GitHub.
  • Suppressions without error codes will continue to suppress any and all errors on their location, but once we roll out the error codes feature, you can add the proper codes to your codebase via a similar process as above. Removing all your old comments and re-adding them with add-comments will include the error code in the newly added comment.