Flow.js学习笔记一

Flow.js
Flow.js是FaceBook发布的开源Javascript静态类型检查器。他给JavaScript提供了静态类型来提高开发人员的生产力和代码质量。
与Vue.js
这个选择最根本的还是在于工程上成本和收益的考量。Vue 2.0 本身在初期的快速迭代阶段是用 ES2015 写的,整个构建工具链也沿用了 Vue 1.x 的基于 ES 生态的一套(Babel, ESLint, Webpack, Rollup...),全部换 TS 成本过高,短期内并不现实。相比之下 Flow 对于已有的 ES2015 代码的迁入/迁出成本都非常低:
1. 可以一个文件一个文件地迁移,不需要一竿子全弄了。2. Babel 和 ESLint 都有对应的 Flow 插件以支持语法,可以完全沿用现有的构建配置;
3. 更贴近 ES 规范。除了 Flow 的类型声明之外,其他都是标准的 ES。万一哪天不想用 Flow 了,用 babel-plugin-transform-flow-strip-types 转一下,就得到符合规范的 ES。
4. 在需要的地方保留 ES 的灵活性,并且对于生成的代码尺寸有更好的控制力 (rollup / 自定义 babel 插件)第三点额外说一些:这一点上 Facebook 应该也是同样的考量,宁可贴近规范而不是用一个被 M$ 控制的、一旦使用难以迁出的语言。为什么 Angular 就愿意用 TS 呢?因为 Angular 本来就是一帮搞 Java 的人弄出来的,而且只是 Google 的一个子项目,Google 在公司层面根本不关心它用什么语言。而对 Facebook 来说 React/ReactNative/Babel/Flow/Nuclide 是整个公司 infrastructure 层面的东西,要么依赖规范,要么就得自己有控制权。编辑器 / IDE 方面,Atom + Nuclide 其实也还凑合,type warning / type hint / autocomplete / jump to definition 都有,就是 Atom 本身慢了点。至于重构、设计什么的,我只想说,看的是使用的人的水平,跟用什么语言没那么大关系。水平烂的人用 TS 一样写的是翔一样的代码,看看 java 就知道了。另外注意我并没有说 TS 不好,但是在 Vue 的需求和现状下 Flow 是更合理的选择。
这是Vue作者关于Flow和Ts的回答。
类型标注
Flow通过类型标注(static type annotations)来检查Javascript代码.这些标注让Flow知道你想在代码中使用何种类型,并且会保证运行的准确性。
// @flow
function square(n: number): number {
return n * n;
}
square("2"); // Error!
实际上因为Flow对Javascript非常了解,并不需要对每个地方进行标注,你只需要做最少的必需的标注,而剩下的地方,Flow会自行推断。大部分时候,Flow不需要任何标注就可以理解你的代码。
// @flow
function square(n) {
return n * n; // Error!
}
square("2");
基本类型
JavaScript有下面几种基本类型 (MDN):
- Booleans
- Strings
- Numbers
- null
- undefined (在Flow中表示为void)
- Symbols (ES2015中新出现的类型,Flow暂不支持)
Maybe类型
Maybe类型是当参数为可选时使用的类型,在参数前加?表示Maybe类型比如?string 或者 ?number。
Maybe类型同样可以接受null或者void作为合法的参数。
可选对象属性
对象类型可以有可选的属性,当属性前面加了?之后.
{ propertyName?: string }
同样作为可选属性,可以为null或者void.
可选函数参数
函数可以有可选的参数,只要在参数后加?
function method(param?: string) { /* ... */ }
除了标注的类型,可选参数可以使用void或无参数,但不可以使用null.
默认函数参数
函数可以使用默认的参数
function method(value: string = "default") { /* ... */ }
字面量类型
除了使用基本类型标注,Flow还可以使用字面量对参数标注。与联合类型结合使用可以实现强大的效果。
// @flow
function getColor(name: "success" | "warning" | "danger") {
switch (name) {
case "success" : return "green";
case "warning" : return "yellow";
case "danger" : return "red";
}
}
getColor("success"); // Works!
getColor("danger"); // Works!
// $ExpectError
getColor("error"); // Error!
混合类型
混合类型,可以接受任何类型作为参数,但需要在函数内对参数进行处理。
// @flow
function stringify(value: mixed) {
// ...
}
stringify("foo");
stringify(3.14);
stringify(null);
stringify({});
Any类型
如果不想对代码进行类型检查,可以使用any标注,但是这样的代码是完全不安全的,尽量避免使用。
下面的代码将不会报错。
// @flow
function add(one: any, two: any): number {
return one + two;
}
add(1, 2); // Works.
add("1", "2"); // Works.
add({}, []); // Works.
可变类型
当你声明一个变量时,可以同时声明他的类型,在JavaScript中有三种声明变量的方法:
- var - 声明一个变量,可选赋值
- let - 声明一个作用域内变量,可选赋值
- const - 声明一个作用域内的变量,必须赋值,而且之后无法改变值。
在Flow中视为两种:
- let 和 var - 可以被再次赋值的变量.
- const - 不可被再次赋值的变量.
重点讲下let和var,当使用let和var声明变量时,如果使用了类型标注,则下面再次赋值时必须按照标注的类型赋值,否则将会报错。
// @flow
let foo: number = 1;
foo = 2; // Works!
// $ExpectError
foo = "3"; // Error!
当为变量重新赋值时,Flow会按照标注提供的所有可能的类型进行检查:
let foo = 42;
if (Math.random()) foo = true;
if (Math.random()) foo = "hello";
let isOneOf: number | boolean | string = foo; // Works!
Flow会判断变量的类型在重新赋值后发生了改变,在这种情况下,Flow会赋予安全的变量类型。
// @flow
let foo = 42;
let isNumber: number = foo; // Works!
foo = true;
let isBoolean: boolean = foo; // Works!
foo = "hello";
let isString: string = foo; // Works!
函数类型
函数有两个地方接受类型标注:参数和返回值
// @flow
function concat(a: string, b: string): string {
return a + b;
}
concat("foo", "bar"); // Works!
// $ExpectError
concat(true, false); // Error!
剩余参数
JavaScript支持使用剩余参数,只需要在最后参数前面加上...,对剩余参数的标注只需要使用Array
function method(...args: Array<number>) {
// ...
}
对象类型
对象在Javascript中被广泛使用,同样对他们的标注也有多种形式。
对象会尽可能符合标注的类型。使用花括号将标注的对象类型括起来,并使用,分割名称和值。
// @flow
var obj1: { foo: boolean } = { foo: true };
var obj2: {
foo: number,
bar: boolean,
baz: string,
} = {
foo: 1,
bar: true,
baz: 'three',
};
可以为一个对象类型设置可选的属性标注
// @flow
var obj: { foo?: boolean } = {};
obj.foo = true; // Works!
// $ExpectError
obj.foo = 'hello'; // Error!
密封的对象
在Flow的语法中,当你创建了一个具有属性的对象,被视为创建了一个密封的对象,将不能向其中添加新的属性。
而如果你创建的是空对象,则可以继续添加新的属性。
准确对象类型
在Flow的语法中,向一个对象标注传递含有额外的属性的对象是被允许的
// @flow
function method(obj: { foo: string }) {
// ...
}
method({
foo: "test", // Works!
bar: 42 // Works!
});
但是有时候我们需要准确的描述对象类型标注,这时就需要准确类型标注的语法:
{| foo: string, bar: number |}
作为maps的对象
最新版本的JavaScript标准中包含了Map类,但是使用对象作为map依然很常见,这种对象允许添加和取回新的属性,并且属性的类型并不确定,所以无法对其进行类型标注。
在这种情况下,Flow使用序号属性的语法为对象类型进行标注。序号属性允许使用符合序号属性类型标注的值对对象进行读写。
// @flow
var o: { [string]: number } = {};
o["foo"] = 0;
o["bar"] = 1;
var foo: number = o["foo"];
序号属性可以被命名,当对象类型含有序号属性时,会假设对象含有指定序号的属性,但是遇到没有该属性的情况下,会在运行时报错。
var obj: { [number]: string } = {};
obj[42].length; // No type error, but will throw at runtime
序号属性可以与命名属性混合使用
// @flow
var obj: {
size: number,
[id: number]: string
} = {
size: 0
};
function add(id: number, name: string) {
obj[id] = name;
obj.size++;
}
浙公网安备 33010602011771号