Flow基本语法
Flow与Typescript
JavaScript 是一门动态类型语言,变量的类型是可以随时改变的;这种灵活性虽然可以使代码编写过程不用太多考虑类型的适配和转变,但是会提高在运行时产生错误的概率;
没有类型的静态检查是 JavaScript 语言的先天缺失,所有很多变量类型相关的问题只能在运行阶段暴露出来。为了使 JavaScript 语言的类型更加安全,业界的实践有 TypeScript;
(什么是TypeScript:https://zhuanlan.zhihu.com/p/63346965 )
这些都需要你重新学习一套语言语法,然后由编译器把相应的代码编译成原生的 JavaScript 代码;在一个现有的系统中把当前代码改成 TypeScript 成本比较高,需要重写所有逻辑。
Facebook 推出的 Flow 是另一个思路。Flow 是一个静态类型检测工具;在现有项目中加上类型标注后,可以在代码阶段就检测出对变量的不恰当使用。Flow 弥补了 JavaScript 天生的类型系统缺陷。利用 Flow 进行类型检查,可以使你的项目代码更加健壮,确保项目的其他参与者也可以写出规范的代码;而 Flow 的使用更是方便渐进式的给项目加上严格的类型检测。
Flow基本类型的类型标注语法
// @flow
const a: string = 'zhihu';
const b: number = 5;
const c: boolean = false;
const d: void = undefined;
const e: null = null;
需要注意的几点:
- undefined 的类型是 void;
- null 的类型是 null;
- string 类型、number 类型和 boolean 类型,其类型名称都是小写开头;但是在 JS 中还有相对应的大写开头的类型名称,如 String,Number, Boolean;
在 Flow 中,大写开头的类型名和小写开头的类型名是有区别的。
// @flow
const a: string = 'a'; // 字面量值对应的类型名称是小写
const b: string = String('b'); // String 函数是将参数转化成一个字符串,仍然是小写的 string 类型
const c: String = new String(‘c’); // 大写开头的类型名称,其对应的值是 new 创建出来的类型实例;
字面值作为一种类型
在 Flow 中,字面量值也可以作为一种类型,符合这种类型的变量只有这个字面量本身;给这个变量赋其他值, Flow 在进行类型检测时都会报错;
// @flow
let monthsAYear: 12 = 12;
monthsAYear = 13; // Flow 会在这里报错
函数类型标注
// @flow
// 函数声明 输入一个字符串返回一个数字
function getLength(str: string): number {
return str.length;
}
// 函数表达式 输入一个字符串返回一个空
const greeting = function(welcome: string): void{
console.log(welcome);
}
// 箭头函数 输入两个数字返回一个数字
const addNumber = (a: number, b: number): number => (a + b);
数组类型标注
对数组的标注 Flow 提供两种语法,
- Array 后接一对尖括号,且尖括号里边就是数组项的类型名称;
- 类型名称后加一对中括号。
// @flex
const names: Array<string> = ['a', 'b', 'c', 'd'];
const ages: number[] = [1, 2, 3, 4];
元组(Tuple)类型的标注
另外一种常见的数组是元组(Tuple)。在其他语言里,元组可能是一个特定的类型结构;但是在 JS 里,元组就是数组来表示的,并且是一个有限数组,数组每一项的类型分别标注出来;通常的使用场景比如一个用来表示数据库纪录的数组,比如函数返回多个返回值。
// @flow
const recordItem : [number, string, boolean] = [1, 'First', true];
一个数组被标注为元组类型后,每一项的类型都不可再次改变,且数组的长度也不能改变;因此对元组类型的数组进行 push、pop 操作,都是 Flow 不允许的。
对象类型的标注
对对象类型加上类型标注的方式是定义其结构(Shape),即有哪些属性,属性及属性值的类型;
// @flow
type BorderConfigType = {
width: number,
color: string,
hasShadow: boolean
}
const borderConfig : BorderConfigType = {
width: 10,
color: 'red',
hasShadow: true,
}
type 是 Flow 中的关键字,用来定义自定义的类型,并且可以在后面的类型标注中使用。
// @flow
type StringType = string;
const name: StringType = ‘zhihu’;
type TupleType = [ number, string ]
const record: TupleType = [ 1, ‘a’ ]
类的标注
Flow支持对ES6中的类进行类型标注,包括类的属性和方法;类中用到的属性必须额外添加类型标注,并且是在与方法同一个层级。
// @flow
class oneClass1{
method() {
this.props = 1; // Flow会报错,因为没有对props进行类型标注
}
}
class oneClass2{
method() {
this.props: number = 1; // Flow 还是会报错,对属性的类型标注必须与方法同一个层级
}
}
class oneClass2{
props:number;
method() {
this.props = 1; // 对了,类型标注和方法在同一个层级
}
}
联结类型·(Union Type)的使用
将两个或两个以上的类型,通过|符号进行联结,可以构成一个联结类型
// @flow
type UserIdType = string | number;
let user_id : UserIdType = 12345678;
user_id = '87654321';
如果是定义函数的参数是一个联结类型,需要在函数的内部针对每种类型都做出判断并进行相应的处理;这个过程称为类型的细化(Type Refinement)
// @flow
type MsgType = string | number;
function show(msg: MsgType) {
if (typeof msg === 'string' ){
// do something
} else {
// 在这个代码块里,可以放心将参数 msg 当成数字类型
// Flow 也会作出这样的推理
}
}
交叉类型(Intersection Type)的使用
将两个或两个以上的类型,通过&符号并列写在一起,可以构成一个交叉类型
// @flow
type C = A & B;
type X1 = 1 | 2 | 3 | 4 | 5
type X2 = 3 | 4 | 5 | 6 | 7
type X3 = X1 & X2; // 3, 4, 5
// @flow
type Y1 = {
name: string,
male: boolean
}
type Y2 = {
name: string,
age: number
}
type Y3 = Y1 & Y2;
符合 Y1 类型的值,必须是一个对象,且必须有一个 name 属性,属性值是字符串类型;且有一个 male 属性,属性值是布尔值类型;
Y2 同理;而 Y3 必须受到 Y1 和 Y2 的约束。因此 Y3 类型的值必须:
- 是一个对象;
- 有 name 属性,属性值是字符串;
- 有 male 属性,属性值是布尔值;
- 有 age 属性,属性值是数字类型。
// @flow
const wrong: Y3 = { // Flow 会报错,因为缺少 male 和 age 属性
name: 'zhihu'
}
const right: Y3 = { // 可以通过 Flow 的类型检测
name: 'zhihu',
male: true,
age: 5
}
对象的可选属性与变量的可选类型
// @flow
type Test = {
key1: ?string, // 必须有 key1 属性,但是属性值不一定的 string,还可以是 null 或者 undefined;
key2?: number, // 表示 Test 类型中,可以没有 key2 属性,但是如果出现了 key2 属性,属性值必须是一个 number 类型
function myFn(t: Test) {
if (t.key1 !== null && t.key1 !== undefined ) {
// 在这里,可以放心的将 t.key1 作为字符串类型进行操作
console.log(t.key1.slice(0,t.key1.length))
}
}
}
在上面的例子中,我们定义了一个 Test 类型;注意它的两个属性后都有个问号,且问号的位置不一样:
一个问号是在冒号后,类型名称前;表示 Test 类型中,必须有 key1 属性,但是属性值不一定的 string,还可以是 null 或者 undefined;从另一个角度讲,只要 key1 值不是 null 和 undefined,就必须是 string 类型。这是 Flow 中 Maybe Type。在对这种类型的变量进行细化的过程中,也必须手工验证值是否为 null 和 undefined。
另一个问号是在冒号前,属性名后;表示 Test 类型中,可以没有 key2 属性,但是如果出现了 key2 属性,属性值必须是一个 number 类型。这是 Flow 中的 Optional object type properties。注意,如果允许没有 key2 属性,在 JavaScript 中,访问对象的某个不存在的属性,会返回 undefined;所以对象的可选属性,其属性值要么是指定的类型(如上例中的 number),要么就是 undefined,不能是 null。
any类型
Flow 提供一个 any 类型;给变量标注为 any 类型后,如你预期的一样,这个变量可以是任意值;基本上相当于告诉 Flow:这个变量的类型我有把握,你不用管。在将 Flow 渐进的应用到你的项目中时,可能 Flow 针对某些变量的报错是不需要处理的,你可以标注为这种类型消除错误。
// @flow
let a: any = 1;
a = ‘a’;
a = { };
引自flow是什么:
https://zhuanlan.zhihu.com/p/26204569