Skip to main content

Flow 覆盖

覆盖率命令提供 Flow 对代码每个部分执行的检查量的度量。具有高 Flow 覆盖率的程序应该会增加你对 Flow 已检测到任何潜在运行时错误的信心。

¥The coverage command provides a metric of the amount of checking that Flow has performed on each part of your code. A program with high Flow coverage should increase your confidence that Flow has detected any potential runtime errors.

其决定因素是每个表达式的推断类型中是否存在 any。推断类型为 any 的表达式被认为是未覆盖的,否则被认为是覆盖的。

¥The determining factor for this is the presence of any in the inferred type of each expression. An expression whose inferred type is any is considered uncovered, otherwise it is considered covered.

要了解为什么选择此指标来确定 Flow 的有效性,请考虑以下示例

¥To see why this metric was chosen for determining Flow's effectiveness, consider the example

1const one: any = 1;2one();

此代码会导致运行时类型错误,因为我们正在尝试对号码执行调用。然而,Flow 在这里不会标记错误,因为我们已将变量 one 注释为 any。只要涉及到 any,Flow 的检查就会被有效关闭,因此它将默默地允许调用。这种不安全类型的使用导致类型检查器无效,覆盖率指标通过将 one 的所有实例报告为未覆盖来揭示这一点。

¥This code leads to a runtime type error, since we are attempting to perform a call on a number. Flow, however, does not flag an error here, because we have annotated variable one as any. Flow's checking is effectively turned off whenever any is involved, so it will silently allow the call. The use of this unsafe type has rendered the type checker ineffective, and the coverage metric is here to surface this, by reporting all instances of one as uncovered.

设计空间

¥Design Space

哪些类型应该是 "覆盖"?

¥Which types should be "covered"?

上面描述的是一种相当粗粒度的确定覆盖范围的方法。人们可以想象一个标准,如果表达式类型的任何部分包含 any(例如 Array<any>),则将表达式标记为未覆盖。虽然这样的指标有价值,但通常会通过对该类型的值进行各种操作来发现该类型的 "裸露" 部分。例如,在代码中

¥What was described above is a rather coarse grained way to determine coverage. One could imagine a criterion that flags expressions as uncovered if any part of their type includes any, for example Array<any>. While there is value in a metric like this, the "uncovered" part of the type will typically be uncovered through various operations on values of this type. For example, in the code

1declare const arr: Array<any>;2arr.forEach(x => {});

参数 x 将被标记为未覆盖。此外,在实践中,像这样的严格标准会噪音太大,并且对于动态计算来说相当昂贵。

¥the parameter x will be flagged as uncovered. Also, in practice, a strict criterion like this would be too noisy and rather expensive to compute on the fly.

联合类型

¥Union types

此原则的一个例外是联合类型:类型 number | any 被认为是未覆盖的,即使技术上 any 不是顶层构造函数。联合仅对一组其他类型中的一个选项进行编码。从这个意义上说,当该表达式的至少一种可能类型导致有限的检查时,我们保守地将表达式视为未发现的。例如,在代码中

¥An exception to this principle are union types: the type number | any is considered uncovered, even though technically any is not the top-level constructor. Unions merely encode an option among a set of other types. In that sense we are conservatively viewing an expression as uncovered, when at least one possible type of that expression causes limited checking. For example, in the code

1let x: number | any = 1;2x = "a";

Flow 允许你将任何内容分配给 x,这会降低使用 x 作为数字的信心。因此 x 被认为是未覆盖的。

¥Flow will let you assign anything to x, which reduces confidence in the use of x as a number. Thus x is considered uncovered.

空类型

¥The empty type

从覆盖范围的角度来看,一个有趣的类型是 empty 类型。这种类型大致对应于死代码。因此,检查 empty 类型的表达式更加轻松,但有充分的理由:该代码不会在运行时执行。由于清理此类代码是一种常见做法,因此 Flow 覆盖率也会报告类型被推断为 empty 的代码,但将其与 any 的情况区分开来。

¥An interesting type from a coverage perspective is the empty type. This type roughly corresponds to dead code. As such checking around expressions with type empty is more relaxed, but for a good reason: this code will not be executed at runtime. Since it is a common practice to clean up such code, Flow coverage will also report code whose type is inferred to be empty, but distinguishes it from the case of any.

命令行使用

¥Command Line Use

查找包含以下内容的文件 foo.js 的覆盖率

¥To find out the coverage of a file foo.js with the following contents

1function add(one: any, two: any): number {2  return one + two;3}4
5add(1, 2);

你可以发送以下命令

¥you can issue the following command

$ flow coverage file.js
Covered: 50.00% (5 of 10 expressions)

此输出意味着该程序的 10 个节点中有 5 个被推断为类型 any。要准确查看哪些部分未被覆盖,你还可以传递以下标志之一:

¥This output means that 5 out of the 10 nodes of this program were inferred to have type any. To see exactly which parts are uncovered you can also pass one of the following flags:

  • --color:这将在终端上打印 foo.js ,并将未覆盖的位置显示为红色。例如。flow coverage --color file.js

    ¥--color: This will print foo.js on the terminal with the uncovered locations in red color. E.g. flow coverage --color file.js

  • --json:这将列出标签 "uncovered_locs" 下未发现的所有位置范围。例如。flow coverage --json file.js

    ¥--json: This will list out all location spans that are uncovered under the tag "uncovered_locs". E.g. flow coverage --json file.js

最后,作为死代码的示例,请考虑以下代码:

¥Finally, as an example of dead code, consider the code:

1function f(x: 'a' | 'b') {2  if (x === 'a') {3    // ...4  } else if (x === 'b') {5    // ...6  } else {7    x;8  }9}

永远不应该达到最后的 else 子句,因为我们已经检查了联合体的两个成员。因此,推断 x 在该分支中具有类型 empty

¥The final else clause should never be reached, as we've already checked for both members of the union. Because of this, x is inferred to have the type empty in that branch.

在此命令的彩色版本中,这些部分显示为蓝色,在 JSON 版本中,它们位于字段 "empty_locs" 下。

¥In the colored version of this command, these parts appear in blue color, and in the JSON version they are under the field "empty_locs".

用于多个文件

¥Use on multiple files

如果你想一次检查多个文件的覆盖范围,Flow 提供了 batch-coverage 命令:

¥If you want to check coverage of multiple files at once, Flow offers the batch-coverage command:

$ flow batch-coverage dir/

将报告 dir/ 下每个文件的覆盖率统计信息以及汇总结果。

¥will report coverage statistics for each file under dir/, as well as aggregate results.

请注意,batch-coverage 需要非惰性 Flow 服务器。

¥Note that batch-coverage requires a non-lazy Flow server.