五个简单例子

在接下例子里,我们将用 flow 命令行来检查代码。 你可以使用 npm 运行

npm install --global flow-bin

从而安装命令行工具

译者:安装略坑,建议


  • mac: brew install flow
  • linux: 如果 npm install 不好使就自己到 github 下源码按照提示 make
  • windows: 官方都没提太多,参考 这里

第一次运行 flow 通常会多花几秒,接下来每次都会快一点。 这是因为运行 flow 会启动一个后台进程, 监测你当前项目,当你每次修改保存后,自动重新计算类型错误。 可以使用 flow stop 来停止这个后台进程

如果你只是想检查一下,而不要一个后台进程, (你不介意每次都多等几秒的话)那么你可以使用 flow check命令。 这样的话,Flow 在检查完你的项目后就立即停止了。

1. Hello Flow!#

在 Flow GitHub 库里, 你可以找到 examples 目录. 这个目录包含了教程所需的例子。先看看第一个例子,感受一下 Flow

$> cd flow/examples/01_HelloWorld
$> flow

你应该看到如下错误:

hello.js:7
  7: foo("Hello, world!");
     ^^^^^^^^^^^^^^^^^^^^ function call
  4:   return x * 10;
              ^ string. This type is incompatible with
  4:   return x * 10;
              ^^^^^^ number

看下例子中的 hello.js 文件, 就明白为什么了:

1
2
3
4
5
6
7
8
9
// @flow

(function() { 
function foo(x) {
  return x * 10;
}

foo('Hello, world!');
}); 

我们调用 foo 方法,使用 string 类型参数,不过 foo 方法期望的参数是 number 类型的。 Flow 能够检测到这个问题并且给出错误提示。解决这个错误的一个方案就是调用 foo 时用 number 类型参数

// @flow

这个注释很关键:它告诉 Flow 这个文件需要检测。 如果某些文件不带这个注释,Flow 则会忽略检测这些文件

2. Add type annotations(类型注释 / 类型注解)#

在大多数情况下,Flow 都能够探测出具体类型,所以你不需要给每个方法、变量都添加类型注解。 虽说 Flow 能探测出类型,但是你依然可以严格声明类型。 只有下面这类情况才 必须 明确地声明类型:变量/方法/类 是从别的模块中导入的(在别的文件定义)

第二个例子 (02_TypeAnnotations) 展示 Flow 简单的类型注释:

1
2
3
4
5
6
7
8
9
// @flow

(function() { 
function foo(x: string, y: number): string {
  return x.length * y;
}

foo('Hello', 42);
}); 

这表示 foo 方法返回值的类型为 string,并且两个参数类型分别为 stringnumber

有了这些类型注释之后,如果运行 flow check 将会看到如下错误提示:

type_annotations.js:4
  4:   return x.length * y;
              ^^^^^^^^^^^^ number. This type is incompatible with the expected return type of
  3: function foo(x: string, y: number): string {
                                         ^^^^^^ string

此时提示 foo 返回值的类型有误。我们声明了返回值为 string 类型, 可是返回的(number)不匹配,于是 Flow 就给出错误提示,可以通过修改返回类型来解决

1
2
3
4
5
6
7
8
9
10
// @flow

(function() { 
// Changing the return type to number fixes the error
function foo(x: string, y: number): number {
  return x.length * y;
}

foo('Hello', 42);
}); 

3. Nullable types#

Flow 处理 null 的方式跟大多数类型系统不太一样。如果检测工具没有仔细地跟踪好 null 的使用, 那么可能会误导你,让你认为你的代码都没问题了,于是在非法使用 null 的时候就悲剧了。

当 Flow 检测到你访问 null 的方式存在危险的话, 它就会给你错误提醒,正如第三个例子(03_Null)

1
2
3
4
5
6
7
8
9
// @flow

(function() { 
function length(x) {
  return x.length;
}

var total = length('Hello') + length(null);
}) 

这个程序在运行时会报 TypeError 错误,因为读取 nulllength 属性是不合法的. 运行 flow 能够提前检测出这个 bug:

nulls.js:7
  7: var total = length("Hello") + length(null);
                                   ^^^^^^^^^^^^ function call
  4:   return x.length;
                ^^^^^^ property `length`. Property cannot be accessed on possibly null value
  4:   return x.length;
              ^ null

answer 目录里的代码给出了解决方案,也许你在运行代码发现错误后也会这么解决。

1
2
3
4
5
6
7
8
9
10
11
12
13
// @flow

(function() { 
function length(x) {
  if (x !== null) {
    return x.length;
  } else {
    return 0;
  }
}

var total = length('Hello') + length(null);
}); 

因为我们已经判断 x 是非 null,Flow 知道现在的代码是安全的,然后就不会出现错误提醒了。

4. Arrays (数组)#

当然,Flow 能检测的类型不仅仅是数字和字符串,下一个例子 04_Arrays,展示如何在函数中 对数组类型进行注解:

1
2
3
4
5
6
7
8
9
10
11
// @flow

function total(numbers: Array<number>) {
  var result = 0;
  for (var i = 0; i < numbers.length; i++) {
    result += numbers[i];
  }
  return result;
}

total([1, 2, 3, 'Hello']);

Flow 会标注 'Hello' 字符串,因为 total() 方法只接受数字组成的数组

arrays.js:11
 11: total([1, 2, 3, "Hello"]);
     ^^^^^^^^^^^^^^^^^^^^^^^^^ function call
 11: total([1, 2, 3, "Hello"]);
                     ^^^^^^^ string. This type is incompatible with
  3: function total(numbers: Array<number>) {
                                   ^^^^^^ number

如果把 "Hello" 改为数字,就能通过 Flow 的检测

1
2
3
4
5
6
7
8
9
10
11
// @flow

function total(numbers: Array<number>) {
  var result = 0;
  for (var i = 0; i < numbers.length; i++) {
    result += numbers[i];
  }
  return result;
}

total([1, 2, 3, 4]);

5. Dynamic code (动态代码)#

最后一个例子 05_DynamicCode,这里我们没有给函数返回类型注释类型, 但是我们在调用的时候传递了两个不同类型的参数:

1
2
3
4
5
6
7
8
9
// @flow

(function() { 
function foo(x) {
  return x.length;
}

var res = foo('Hello') + foo(42);
}); 

这种情况,Flow 检测到第二次调用的参数是数字,它木有 length 属性,铁定会挂

dynamic.js:4
  4:   return x.length;
                ^^^^^^ property `length`. Property not found in
  4:   return x.length;
              ^ Number

一个简单的解决方式就是作类型判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
// @flow

(function() { 
function foo(x) {
  if (typeof x === 'string') {
    return x.length;
  } else {
    return x;
  }
}

var res = foo('Hello') + foo(42);
}); 

Flow 很聪明,能够明白你的条件语句规避了潜在的悲剧,于是就啥错误都没了。

下一步#

这些浅显的例子算是扒开了 Flow,以后在新项目就可以玩 Flow,然后在发布生产前用 转换工具 编译类型注解。 当然,你也可以 给老项目用上 FLow。 你还可以看看这个稍微大点的 React 例子,了解更多典型的用法

← Prev Next →

You can edit this page on GitHub and send us a pull request!