五个简单例子
在接下例子里,我们将用 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
,并且两个参数类型分别为 string
和 number
有了这些类型注释之后,如果运行 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
错误,因为读取 null
的 length
属性是不合法的.
运行 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 例子,了解更多典型的用法
You can edit this page on GitHub and send us a pull request!