フローエラー抑制をより具体的にする

Mar 19 2020
誤ってエラーを隠さないように、フローエラー抑制を改善しています。来年、Flowは型システムに多くの変更と改善を加えることを計画しています。
Pexelsのicon0.comによる写真

誤ってエラーを隠さないように、フローエラー抑制を改善しています。

来年、Flowはその型システムに多くの変更と改善を加えることを計画しています。ただし、これによる結果の1つは、Flowが既存の問題をより適切に検出できるようになるため、コードに抑制を追加する必要がある場合があることです。

Flowの抑制は、抑制しようとした元のエラー以外にも影響を与えるため、これは問題になる可能性があります。これにより、フローがエラーをキャッチする機能が低下します。ことをまず、この2つのプロパティの結果であるフロー抑止タイプエラーの任意の部分に配置することができる、ということ、及び第二フロー抑止タイプエラーのあらゆる種類のを抑制することができます。これらの欠点を修正するには、エラーが実際に発生するコード内の場所に抑制を配置し、抑制したいエラーの種類を指定するエラーコードを含める必要があります。

これが何を意味するのかを理解するために、フローのタイプエラーの性質を掘り下げてみましょう。

タイプエラーとは何ですか?

2つのタイプ間の非互換性や無効なプロパティアクセスなど、コードのチェック中にFlowで問題が発生すると、エラーが生成され、コードの何が問題になっているのかが正確にユーザーに報告されます。これらのエラーには、原因となった特定の状況に応じてさまざまな情報が含まれている可能性がありますが、私たちが最も気にする2つは、エラーの種類とエラーの場所です。エラー

種類は、Flowがコードで検出した不正な動作の特定の性質をエンコードします。これは、関数に引数を渡しすぎるような単純なものでも、共用体型の拡散などの複雑なものでもかまいません。この情報は、間違った場所を正確に説明するためにユーザーに表示する情報の種類をFlowに正確に伝えます。エラー

場所には、コード内のエラーの場所に関する情報が含まれています。エラーに含まれる可能性のある場所には、プライマリの場所(1つしか存在できない)とセカンダリの場所の2種類があり、その場所は多数ある場合とまったくない場合があります。プライマリロケーションは、エラーが実際に発生したコード内のロケーションにほぼ対応し、セカンダリロケーションは、エラーに関係するタイプと値の定義サイトに関する情報を伝達します。例えば:

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]

では、抑制の何がそんなに悪いのでしょうか?

まあ、抑制はプライマリとセカンダリの両方の場所に適用できるので//$FlowFixMe、の定義の上にコメントを置くfooと、上記の両方の例のエラーが抑制されます。それfooを使用するすべてのエラーでセカンダリロケーションとして表示さfooれるため、エラーが発生する可能性のある場所では、この1つの抑制によってエラーが発生することを意味します。foo任意のタイプの任意の数の引数を使用して呼び出すことができ、Flowはこれに関するエラーや警告を表示しません。これにより、Flowの型チェックに対するユーザーの信頼が低下し、他の方法では検出されたはずのバグが本番環境に組み込まれる可能性があります。

これに加えて、フローの抑制は、抑制がカバーする場所が含まれている限り、あらゆる種類のエラーに影響します。これは、異なる種類の複数のエラーを1回の抑制で抑制できることを意味します。これの危険性を確認するには、次の例を検討してください。

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

これについてどうしますか?

これに対処するために、次の変更を行います。

  • プライマリロケーションを適用します。抑制が主要な場所に配置された場合にのみエラーに適用されるように、抑制動作を変更します。たとえば、関数の定義で無効な呼び出しを抑制できるようにする代わりに、抑制を呼び出しサイト自体に配置する必要があります。このようにして、Flowは、関数への他の無効な可能性のある呼び出しについて警告することができます。
  • エラーコードを追加します。また、指定された種類のエラーのみを抑制するように、抑制にエラーコードを追加します。これにより、抑制によって、予期していなかった種類のエラーが予期せず抑制されるのを防ぐことができます。
  • 抑制構文を標準化します。以前は、エラー抑制の許容可能な構文を.flowconfig特定のプロジェクトに対して手動で構成でき、プロジェクト間で一貫性のない抑制構文を使用できました。上記の変更の一環として、抑制構文も標準化する予定です。$FlowFixMe[incompatible-type]または$FlowExpectedError[incompatible-type]形式のみをサポートします。

ロールアウトする

この変更により、コードベース内のかなりの数のエラーが無効になる可能性があることを認識しています。特に、ライブラリコードに抑制を配置する習慣がある場合はそうです。この負担を軽減するために、いくつかの提案があります。

  • 新しく無効になった抑制をプライマリの場所に再配置するには、フローツールで提供されるadd-commentsremove-commentsユーティリティの組み合わせをお勧めします。実行./tool remove-commentsすると、プライマリロケーションにないためにエラーを抑制しなくなったコメントが削除され、抑制されていない./tool add-comments場所に新しい抑制が配置されます。./toolこのスクリプトは、GitHubの上のフローリポジトリをクローンしてアクセスすることができます。
  • エラーコードのない抑制は、その場所のすべてのエラーを抑制し続けますが、エラーコード機能を展開すると、上記と同様のプロセスを介して適切なコードをコードベースに追加できます。古いコメントをすべて削除して再度追加するとadd-comments、新しく追加されたコメントにエラーコードが含まれます。