0%

flow笔记

简介

JavaScript作为一种脚本语言是没有参数类型这个概念的,所以在编写代码的时候可以给参数赋予任何类型的值。在写JS的时候不用考虑参数类型、参数定义等等概念。当项目逐渐变大,人员逐渐变动的时候,没有任何参数类型再加上代码命名不规范,代码将变得十分难以维护。面对一个参数变量不知道它是用来做什么的。可能会有如下代码:

1
2
3
4
5
6
7
8
main = () => {
//fn1函数获取了一个数据
var object = fn1()
//fn2根据获数据,产生一个结果
var result = fn2(object)

return result
}

Flow是个JavaScript的静态类型检查工具,所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,又不影响代码运行(不需要运行时动态检查类型),使编写 JavaScript 具有和编写强类型语言相近的体验。

由于之前是写Objective-C的,对于类型检查还是比较能接受的。虽然在编写代码的时候会繁琐一些,但是由于提前声明了参数的类型,使用起来会更加的“放心”。


以下代码实践均在react native项目中实践

安装

1
npm install --save-dev flow-bin

创建配置文件。

1
touch .flowconfig

先不管空白的*.flowconfig配置文件。在package.json*文件里flow脚本。
your project/package.json

1
2
3
"scripts": {
"flow": "flow; test $? -eq 0 -o $? -eq 2",
},

然后给需要flow检查的文件里加上//@flow或者/*@flow*/。然后就可以检查了。(也可以在命令中加上–all, 这样就会检查所有文件)。

在根目录下运行命令:

1
npm run flow

以上就配置好了flow

使用

Flow最重要的功能就是允许在参数前添加类型注释,即规定该参数的数据类型。Flow内置了很多的数据类型,有些是给原生类型用的,像 numberstringanymixed 比较宽松,没有把值的类型限定死,而其他字面类型则描述某一种类型。

基本数据类型

Flow中常用的基本数据类型有numberstringboolean

注意,这里的类型都是小写开头的

他们的用法大致相同。都是在参数中声明该参数的类型,下面是一个使用number的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// js
plus = (x, y) => {
return x + y;
}

// @flow
plus = (x: number, y: number) : number => {
return x + y;
}
plus(1, 2); // 正常返回3
plus('1', 2); // 报错:Cannot call `plus` with `'1'` bound to `x` because string [1] is incompatible with number [2].

// 可以看到使用了Flow之后,当传入了不符合预先声明的类型时Flow就会报错,当然这些错误并不影响代码的运行。
// 不仅可以在定义函数的时候使用,而且可以在初始化变量的时候,就指定这个参数的类型,这和强类型语言的体验相似
let number1: number = 5;
let number2: number = '5'; // 报错

复合数据类型

Flow中也内置了相关的复合数据类型,如ArrayObjectFunction

注意,这里的类型都是大写开头的

他们的使用方式和基础数据类型差不多,不过Array在声明的时候,不仅可以声明这个参数是数组类型,还可以声明这个数组里面的数据类型。

1
2
3
4
5
6
7
8
// @flow
let List1: Array<number> = [1];
let List2: Array<string> = ['21', '22'];
let List3: Array<boolean> = [false];

getFirst = (list: Array<number>) : number => {
return list[0];
}

任何数据类型

当不确定一个参数的类型时,可以使用mixed来声明该参数,但是在使用的时候必须要判断这个参数的类型,否则Flow会报错。

1
2
3
4
5
6
7
8
9
// @flow
plus = (y: mixed) : number => {
if (typeof y === 'number') {
return y;
} else {
return 1;
}
}
// mixed是混合类型的意思

如果不希望这个参数被Flow检查,可以使用 any即任意类型。如果频繁使用any来声明参数类型,那么Flow就没有任何意义了,尽量还是避免使用any

枚举类型

Flow支持自定义的一些参数类型,不仅仅局限于它内置的几个。可以使用Flow轻松的实现一个枚举类型。

1
2
3
4
// @flow
type myType = 'A' | 'B' | 'C';
let testType: myType = 'A';
// 定一个枚举,声明的参数如果是该枚举类型,那么它的值一定是在枚举类型中。

可选的参数类型

当一个参数不确定是什么类型的时候,上述的mixed可以实现但是并不常用,通常使用的是|来声明多个参数类型,或者使用 ?来代替mixed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// @flow
plus = (y: ?number) : number => {
if (typeof y === 'number') {
return y;
} else {
return 1;
}
}
// 这个?和mixed是一样的,意思是这个参数可能是number类型,当然也有可能是其他的类型。那么在使用的时候就需要对参数的类型判断
// 如果参数的类型有两个,也就是说可能是number类型也可能是string类型,可以使用|来将所有的可能的参数类型列举出来
plus = (y: number | string) : number => {
if (typeof y === 'number') {
return y;
} else {
return 1;
}
}
// 和上述的一样,使用之前都需要判断参数的类型才能使用

关于?的使用

Flow中的?有两种使用方法,一个是在参数前表明这个参数是可选的,另一个是在数据类型前表示可能是这个参数类型,也可能是其他的类型。

1
2
3
4
5
6
7
8
// @flow
let number2: ?number = 43; // 意思是这个参数有可能是number类型
let string2: ?string = '12';

plus = (x?: number, y: number) : number => {
return x + y;
}
// 这里的?意思是这个x参数可能不存在

在React Native中使用

在React Native自定义组件的时候,Flow可以用来声明其需要的属性,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// @flow
// 该组件的属性
type Props = {
text?: string,
pointerEvents?: boolean,
timeout?: number,
onLoadingTimeout?: Function,
offsetY?: number,
};
// 组件
export default class Loading extends React.Component<Props> {
static defaultProps = {
pointerEvents: false,
timeout: 0,
offsetY: 0,
};
}

在自定义组件中不仅可以定义从父组件传递的参数,还可以定义默认属性。其他的用法和上述用法是相同的。

总结

Flow本质上也只是个检查工具,它并不会自动修正代码中的错误,也不会强制说你没按照它的警告消息修正,就不会让你运行程序。当然,并没有要求什么时候一定要用这类的工具,只是这种作法可以让你的代码更具强健性与提高阅读性,也可以直接避去很多不必要的数据类型使用上的问题,这种开发方式目前在许多框架与函数库项目,或是以JavaScript应用为主的开发团队中都已经都是必用工具,在React、Vue源码中均在使用。

参考资料