Haciendo más específicas las supresiones de errores de flujo

Mar 19 2020
Estamos mejorando la supresión de errores de flujo para que no oculten errores accidentalmente. Durante el próximo año, Flow planea realizar muchos cambios y mejoras en su sistema de tipos.
Foto de icon0.com en Pexels

Estamos mejorando la supresión de errores de flujo para que no oculten errores accidentalmente.

Durante el próximo año, Flow tiene previsto realizar muchos cambios y mejoras en su sistema de tipos . Sin embargo, una de las consecuencias de esto es que es posible que deba agregar más supresiones a su código a medida que Flow mejora en la búsqueda de problemas existentes.

Esto puede ser un problema porque las supresiones de Flow afectan más que solo el error original que pretendía eliminar. Esto reduce la capacidad de Flow para detectar errores. Esto es el resultado de dos propiedades: primero, que las supresiones de flujo se pueden ubicar en cualquier parte de un error de tipo, y segundo, que las supresiones de flujo pueden suprimir cualquier tipo de error de tipo . Para solucionar estas deficiencias, vamos a requerir que las supresiones se coloquen en la ubicación de su código donde realmente ocurre el error, y que incluyan un código de error que especifique el tipo de error que desea eliminar.

Para entender qué significa todo esto, profundicemos en la naturaleza de los errores de tipo de Flow.

¿Qué es un error de tipo?

Cuando Flow encuentra un problema mientras verifica su código, como una incompatibilidad entre dos tipos o un acceso de propiedad no válido, genera un error que informa al usuario con precisión qué está mal en su código. Estos errores pueden contener una amplia variedad de información dependiendo de las circunstancias particulares que los causaron, pero los dos que más nos preocupan son el tipo de error y la ubicación del error.

El tipo de error codifica la naturaleza específica del mal comportamiento que Flow detectó en su código, ya sea algo simple como pasar demasiados argumentos a una función o algo complejo como difundir un tipo de unión. Esta información le dice a Flow exactamente qué tipo de información mostrar al usuario para explicar mejor exactamente dónde se equivocó.

Las ubicaciones del error contienen información sobre dónde se encuentra el error en su código. Hay dos tipos de ubicaciones que puede contener un error, una ubicación principal (de la cual solo puede haber una) y ubicaciones secundarias , de las cuales puede haber muchas o ninguna. La ubicación principal corresponde aproximadamente a la ubicación en su código donde realmente ocurrió el error, mientras que las ubicaciones secundarias contienen información sobre los sitios de definición de los tipos y valores involucrados en el error. Por ejemplo:

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]

Entonces, ¿qué tienen de malo las supresiones?

Bueno, debido a que las supresiones se pueden aplicar tanto en la ubicación principal como en la secundaria, poner un //$FlowFixMecomentario sobre la definición de foosuprimiría los errores en los dos ejemplos anteriores. Dado que fooaparece como una ubicación secundaria en cada error que lo usa, eso significaría que cualquier uso de fooese resultado en un error sería suprimido por esta supresión, en cualquier lugar donde pudiera ocurrir. Podrías llamar foocon cualquier número de argumentos de cualquier tipo y Flow no mostraría ningún error o advertencia sobre nada de esto. Esto degrada la confianza de los usuarios en la verificación de tipos de Flow y permite que los errores que, de otro modo, hubieran detectado, pasen a producción.

Además de esto, las supresiones de Flow afectan a todo tipo de errores, siempre que contengan una ubicación que cubra la supresión. Esto significa que una sola supresión puede suprimir múltiples errores de diferentes tipos. Para ver el peligro en esto, considere el siguiente ejemplo:

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

¿Qué vamos a hacer al respecto?

Para abordar esto, vamos a realizar los siguientes cambios:

  • Hacer cumplir las ubicaciones principales. Cambiaremos el comportamiento de supresión de modo que las supresiones solo se apliquen a los errores cuando se coloquen en su ubicación principal. Por ejemplo, en lugar de permitirle suprimir una llamada no válida en la definición de la función, ahora requeriremos que la supresión se ubique en el propio sitio de la llamada. De esta manera, Flow aún podrá alertarle sobre otras llamadas posiblemente no válidas a la función.
  • Agrega códigos de error. También agregaremos códigos de error a nuestras supresiones para que solo supriman los errores del tipo especificado. Esto evitará que las supresiones supriman inesperadamente un tipo de error que no esperaba encontrar.
  • Estandarice la sintaxis de supresión. Anteriormente, la sintaxis aceptable para una supresión de errores se podía configurar manualmente en el .flowconfigpara un proyecto determinado, lo que permitía una sintaxis de supresión inconsistente en todos los proyectos. Como parte de los cambios anteriores, también estandarizaremos la sintaxis de supresión; solo soporta los formatos $FlowFixMe[incompatible-type]o $FlowExpectedError[incompatible-type].

Desenrollar

Reconocemos que este cambio puede causar que una cantidad significativa de errores en sus bases de código dejen de ser válidos, especialmente si tenía el hábito de colocar supresiones en el código de la biblioteca. Para aliviar esta carga, tenemos un par de sugerencias:

  • Para reubicar sus nuevas supresiones no válidas en sus ubicaciones principales, recomendamos una combinación de las utilidades add-commentsy remove-commentsproporcionadas en la herramienta Flow. La ejecución ./tool remove-commentseliminará los comentarios que ya no supriman un error porque no se encuentran en una ubicación principal y ./tool add-commentscolocará nuevas supresiones en las ubicaciones no suprimidas. Se ./toolpuede acceder al script clonando el repositorio de Flow en GitHub.
  • Las supresiones sin códigos de error continuarán suprimiendo todos y cada uno de los errores en su ubicación, pero una vez que implementemos la función de códigos de error, puede agregar los códigos adecuados a su base de código mediante un proceso similar al anterior. Eliminar todos sus comentarios antiguos y volver a agregarlos con add-commentsincluirá el código de error en el comentario recién agregado.