Tornando as supressões de erro de fluxo mais específicas

Mar 19 2020
Estamos aprimorando as supressões de erro do Flow para que não ocultem erros acidentalmente. Durante o próximo ano, a Flow está planejando fazer muitas mudanças e melhorias em seu sistema de tipos.
Foto de icon0.com de Pexels

Estamos aprimorando as supressões de erro do Flow para que não ocultem erros acidentalmente.

Durante o próximo ano, a Flow está planejando fazer muitas mudanças e melhorias em seu sistema de tipos . Uma das consequências disso, entretanto, é que você pode precisar adicionar mais supressões ao seu código conforme o Flow fica melhor na localização dos problemas existentes.

Isso pode ser um problema porque as supressões de Flow afetam mais do que apenas o erro original que você pretendia suprimir. Isso reduz a capacidade do Flow de detectar erros. Isso é resultado de duas propriedades: primeiro, que as supressões de fluxo podem estar localizadas em qualquer parte de um erro de tipo e, segundo, que as supressões de fluxo podem suprimir qualquer tipo de erro de tipo . Para corrigir essas deficiências, vamos exigir que as supressões sejam colocadas no local do seu código onde o erro realmente ocorre e que incluam um código de erro especificando o tipo de erro que você deseja suprimir.

Para entender o que tudo isso significa, vamos nos aprofundar na natureza dos erros de tipo de Flow.

O que é um erro de tipo?

Quando o Flow encontra um problema ao verificar seu código, como uma incompatibilidade entre dois tipos ou um acesso de propriedade inválido, ele gera um erro que informa ao usuário exatamente o que há de errado com seu código. Esses erros podem conter uma grande variedade de informações, dependendo das circunstâncias específicas que os causaram, mas os dois mais importantes para nós são o tipo e a localização do erro.

O tipo de erro codifica a natureza específica do mau comportamento que o Flow detectou em seu código, seja algo simples como passar muitos argumentos para uma função ou algo complexo como espalhar um tipo de união. Essas informações informam ao Flow exatamente que tipo de informação exibir ao usuário para melhor explicar exatamente onde você errou.

Os locais do erro contêm informações sobre onde o erro está localizado em seu código. Existem dois tipos de locais que um erro pode conter, um local principal (dos quais pode haver apenas um) e locais secundários , dos quais pode haver muitos ou nenhum. O local primário corresponde aproximadamente ao local em seu código onde o erro realmente ocorreu, enquanto os locais secundários carregam informações sobre os sites de definição dos tipos e valores envolvidos no erro. Por exemplo:

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]

Então, o que há de tão errado com as supressões?

Bem, como as supressões podem ser aplicadas nos locais primário e secundário, colocar um //$FlowFixMecomentário acima da definição de foosuprimiria os erros em ambos os exemplos acima. Visto que fooaparece como um local secundário em cada erro que o usa, isso significaria que qualquer uso fooque resulte em um erro seria suprimido por essa supressão, em qualquer lugar em que pudesse ocorrer. Você seria capaz de chamar foocom qualquer número de argumentos de qualquer tipo e o Flow não apresentaria nenhum erro ou aviso para você sobre nada disso. Isso degrada a confiança dos usuários na verificação de tipo do Flow e permite que bugs que ele teria detectado entrem em produção.

Além disso, as supressões de Flow afetam todos os tipos de erros, desde que contenham um local coberto pela supressão. Isso significa que vários erros de diferentes tipos podem ser suprimidos por uma única supressão. Para ver o perigo nisso, considere o seguinte exemplo:

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

O que vamos fazer sobre isso?

Para resolver isso, vamos fazer as seguintes alterações:

  • Aplicar locais primários. Estaremos mudando o comportamento de supressão de forma que as supressões só se apliquem a erros quando colocados em seu local principal. Por exemplo, em vez de permitir que você suprima uma chamada inválida na definição da função, agora exigiremos que a supressão esteja localizada no próprio site da chamada. Dessa forma, o Flow ainda poderá alertá-lo sobre outras chamadas possivelmente inválidas para a função.
  • Adicione códigos de erro. Também estaremos adicionando códigos de erro às nossas supressões para que eles suprimam apenas os erros do tipo especificado. Isso impedirá que as supressões suprimam inesperadamente um tipo de erro que você não esperava encontrar.
  • Padronize a sintaxe de supressão. Anteriormente, a sintaxe aceitável para uma supressão de erro podia ser configurada manualmente no .flowconfigpara um determinado projeto, permitindo sintaxe de supressão inconsistente entre projetos. Como parte das alterações acima, também padronizaremos a sintaxe de supressão; suportando apenas os formatos $FlowFixMe[incompatible-type]ou $FlowExpectedError[incompatible-type].

Sair da cama

Reconhecemos que essa alteração pode fazer com que um número significativo de erros em suas bases de código se tornem inválidos, especialmente se você tinha o hábito de colocar supressões no código da biblioteca. Para aliviar esse fardo, temos algumas sugestões:

  • Para realocar suas supressões inválidas recentemente para seus locais primários, recomendamos uma combinação dos utilitários add-commentse remove-commentsfornecidos na ferramenta Flow. A execução ./tool remove-commentsremoverá todos os comentários que não suprimem mais um erro por não estarem em um local principal e ./tool add-commentscolocará novas supressões em locais não suprimidos. O ./toolscript pode ser acessado clonando o repositório Flow no GitHub.
  • As supressões sem códigos de erro continuarão a suprimir todo e qualquer erro em sua localização, mas assim que lançarmos o recurso de códigos de erro, você poderá adicionar os códigos adequados à sua base de código por meio de um processo semelhante ao anterior. Remover todos os seus comentários antigos e adicioná-los novamente com add-commentsincluirá o código de erro no comentário recém-adicionado.