简介
JavaScript作为一种脚本语言是没有参数类型这个概念的,所以在编写代码的时候可以给参数赋予任何类型的值。在写JS的时候不用考虑参数类型、参数定义等等概念。当项目逐渐变大,人员逐渐变动的时候,没有任何参数类型再加上代码命名不规范,代码将变得十分难以维护。面对一个参数变量不知道它是用来做什么的。可能会有如下代码:
main = () => {
//fn1函数获取了一个数据
var object = fn1()
//fn2根据获数据,产生一个结果
var result = fn2(object)
return result
}
Flow是个JavaScript的静态类型检查工具,所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,又不影响代码运行(不需要运行时动态检查类型),使编写 JavaScript 具有和编写强类型语言相近的体验。
由于之前是写Objective-C的,对于类型检查还是比较能接受的。虽然在编写代码的时候会繁琐一些,但是由于提前声明了参数的类型,使用起来会更加的“放心”。
以下代码实践均在react native
项目中实践
安装
npm install --save-dev flow-bin
创建配置文件。
touch .flowconfig
先不管空白的.flowconfig配置文件。在package.json文件里flow脚本。
your project/package.json
"scripts": {
"flow": "flow; test $? -eq 0 -o $? -eq 2",
},
然后给需要flow检查的文件里加上//@flow
或者/*@flow*/
。然后就可以检查了。(也可以在命令中加上--all, 这样就会检查所有文件)。
在根目录下运行命令:
npm run flow
以上就配置好了flow
使用
Flow最重要的功能就是允许在参数前添加类型注释,即规定该参数的数据类型。Flow内置了很多的数据类型,有些是给原生类型用的,像 number
和 string
。 any
和 mixed
比较宽松,没有把值的类型限定死,而其他字面类型则描述某一种类型。
基本数据类型
Flow中常用的基本数据类型有number
、string
、boolean
注意,这里的类型都是小写开头的
他们的用法大致相同。都是在参数中声明该参数的类型,下面是一个使用number
的例子。
// 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中也内置了相关的复合数据类型,如Array
、Object
、Function
注意,这里的类型都是大写开头的
他们的使用方式和基础数据类型差不多,不过Array
在声明的时候,不仅可以声明这个参数是数组类型,还可以声明这个数组里面的数据类型。
// @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会报错。
// @flow
plus = (y: mixed) : number => {
if (typeof y === 'number') {
return y;
} else {
return 1;
}
}
// mixed是混合类型的意思
如果不希望这个参数被Flow检查,可以使用any
即任意类型。如果频繁使用any
来声明参数类型,那么Flow就没有任何意义了,尽量还是避免使用any
。
枚举类型
Flow支持自定义的一些参数类型,不仅仅局限于它内置的几个。可以使用Flow轻松的实现一个枚举类型。
// @flow
type myType = 'A' | 'B' | 'C';
let testType: myType = 'A';
// 定一个枚举,声明的参数如果是该枚举类型,那么它的值一定是在枚举类型中。
可选的参数类型
当一个参数不确定是什么类型的时候,上述的mixed
可以实现但是并不常用,通常使用的是|
来声明多个参数类型,或者使用?
来代替mixed
// @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中的?
有两种使用方法,一个是在参数前表明这个参数是可选的,另一个是在数据类型前表示可能是这个参数类型,也可能是其他的类型。
// @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可以用来声明其需要的属性,比如
// @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源码中均在使用。