遍寻百度,google,没发现flow的中文文档,这对国内显然是不友好的,虽然说flow 平时用不着, 可是通常框架都会用一下,以便用户能够准确的使用框架,能够避免不少谜同样的BUG,既然没有,那我就来翻译一下咯.计划先翻译类型注释(types annotations)部分,安装的一搜一大把.javascript
当你的类型不注释的时候, flow 就不起做用了,so 来看看 flow 类型 能够如何注释. 可不是 // 这个注释java
javascript 一共有6钟原始数据类型.git
原始类型分两种,一种是字面量的, 一种是包装过的 好比 3 跟 Number(3);
好比下面这样,你只能传 字面量.booleans 除外es6
// @flow function method(x: number, y: string, z: boolean) { // ... } method(3.14, "hello", true);
flow 能够识别 !!x 和 Boolean(0) 的明确类型转换的booleanexpress
// @flow function acceptsBoolean(value: boolean) { // ... } acceptsBoolean(0); // Error! 错误 acceptsBoolean(Boolean(0)); // Works! OK acceptsBoolean(!!0); // Works! OK
数字就很明确了数组
// @flow function acceptsNumber(value: number) { // ... } acceptsNumber(42); // Works! acceptsNumber(3.14); // Works! acceptsNumber(NaN); // Works! acceptsNumber(Infinity); // Works! acceptsNumber("foo"); // Error!
// @flow function acceptsString(value: string) { // ... } acceptsString("foo"); // Works! acceptsString(false); // Error!
js中 字符串会有隐藏的转换安全
"foo" + 42; // "foo42" "foo" + {}; // "foo[object Object]"
flow 只支持数字和字符串的隐藏转换数据结构
// @flow "foo" + "foo"; // Works! "foo" + 42; // Works! "foo" + {}; // Error! "foo" + []; // Error!
若是你要使用, 必需要明确转换框架
// @flow "foo" + String({}); // Works! "foo" + [].toString(); // Works! "" + JSON.stringify({}) // Works!
在flow中 undefined 是 voiddom
// @flow function acceptsNull(value: null) { /* ... */ } function acceptsUndefined(value: void) { /* ... */ } acceptsNull(null); // Works! acceptsNull(undefined); // Error! acceptsUndefined(null); // Error! acceptsUndefined(undefined); // Works!
可能的类型, 是要用再 那些可选的值, 你可使用一个问号来标记他, 证实这个值是可选的,并非必须的
// @flow function acceptsMaybeString(value: ?string) { // ... } acceptsMaybeString("bar"); // Works! acceptsMaybeString(undefined); // Works! acceptsMaybeString(null); // Works! acceptsMaybeString(); // Works!
你能够用一个问号来表示该对象的某个属性是无关紧要的
// @flow function acceptsObject(value: { foo?: string }) { // ... } acceptsObject({ foo: "bar" }); // Works! acceptsObject({ foo: undefined }); // Works! acceptsObject({ foo: null }); // Error! acceptsObject({}); // Works!
这个值能够是undefined 可是 他不能是null
加个问号标明,这个函数的参数可可选的
// @flow function acceptsOptionalString(value?: string) { // ... } acceptsOptionalString("bar"); // Works! acceptsOptionalString(undefined); // Works! acceptsOptionalString(null); // Error! acceptsOptionalString(); // Works!
es5 的新特性
// @flow function acceptsOptionalString(value: string = "foo") { // ... } acceptsOptionalString("bar"); // Works! acceptsOptionalString(undefined); // Works! acceptsOptionalString(null); // Error! acceptsOptionalString(); // Works!
flow未支持
flow 不止能够指定类型, 他还能够指定某个特定的值. 很是牛掰
如:
// @flow function acceptsTwo(value: 2) { // ... } acceptsTwo(2); // Works! // $ExpectError acceptsTwo(3); // Error! // $ExpectError acceptsTwo("2"); // Error!
如:
// @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!
你能够匹配多个类型
function stringifyBasicValue(value: string | number) { return '' + value; }
你能够像java 的泛型同样(有区别)去标明一个类型,下面的例子 标明,该函数返回的类型跟传进函数的类型相同.
function identity<T>(value: T): T { return value; }
你能够这样来 标记 一个函数能够接受任何类型的参数
function getTypeOf(value: mixed): string { return typeof value; }
当你使用 mixed 时候, 虽然你能够传进任何类型, 可是你返回的时候 必需要明确他是什么类型, 否则就报错
// @flow function stringify(value: mixed) { // $ExpectError return "" + value; // Error! } stringify("foo");
不要搞混any 和mixed, 若是你想跳过类型检查,那你就用 any 吧
// @flow function add(one: any, two: any): number { return one + two; } add(1, 2); // Works. add("1", "2"); // Works. add({}, []); // Works.
只要两种状况,可使用 any
当你声明了 传进的参数的any的时候,那么你返回的参数也都是any , 避免这种状况, 须要切断 它
// @flow function fn(obj: any) /* (:number) */ { let foo: number = obj.foo; // 这句才是重点, 切断 any let bar /* (:number) */ = foo * 2; return bar; } let bar /* (:number) */ = fn({ foo: 2 }); let baz /* (:string) */ = "baz:" + bar;
就是上面提到的 能够用 ? 问号标记他是可选的类型
在flow 分为两组, 一组是 let 和 var 能够再次赋值, 另外一组是const 不能再次赋值
const 能够注入你赋值的类型, 或者你本身手动的指定类型
// @flow const foo /* : number */ = 1; const bar: number = 2;
跟上面同样, 这两个也能够自动的注入类型
可是 若你自动注入的类型, 你从新赋值修改的类型的时候并不会获得报错
若是语句,函数,和其余的条件代码,能够精确的指出他是什么类型,那么就能够避免flow 的检查 否则就报错
// @flow let foo = 42; function mutate() { foo = true; foo = "hello"; } mutate(); // $ExpectError let isString: string = foo; // Error!
尽可能避免上面的用法(我的推荐)
函数只有两种用法, 要么参数, 要么返回值
// @flow function concat(a: string, b: string): string { return a + b; } concat("foo", "bar"); // Works! // $ExpectError concat(true, false); // Error!
同上
(str: string, bool?: boolean, ...nums: Array<number>) => void
function method(callback: (error: Error | null, value: string | null) => void) { // 上面代表, 这和函数接受的参数 只能是 error null 和 string 而后染回一个 undefined }
// @flow function method(optionalValue?: string) { // ... } method(); // Works. method(undefined); // Works. method("string"); // Works. // $ExpectError method(null); // Error!
function method(...args: Array<number>) { // ... 使相似java 泛型的 样子 来声明他的类型 }
function method(): number { // 如果标明了返回类型, 那么你的函数必定要有返回值,若是有条件判断语句,那么每一个条件都要有返回值 }
你不用注释this flow 会自动检测上下文来肯定 this 的类型
function method() { return this; } var num: number = method.call(42); // $ExpectError var str: string = method.call(42);
可是下面这种状况,flow 就会报错
function truthy(a, b): boolean { return a && b; } function concat(a: ?string, b: ?string): string { if (truthy(a, b)) { // $ExpectError 问题出现再truthy 上 多是 通过了隐式的类型转换 return a + b; } return ''; }
你能够这样来修复上面的问题, 再truthy 上使用 %check
function truthy(a, b): boolean %checks { return !!a && !!b; } function concat(a: ?string, b: ?string): string { if (truthy(a, b)) { return a + b; } return ''; }
若是你想跳过 flow 的 类型检查 , 除了用any 再函数上你还能够用 Function 不过这是不稳定的,你应该避免使用他
function method(func: Function) { func(1, 2); // Works. func("1", "2"); // Works. func({}, []); // Works. } method(function(a: number, b: number) { // ... });
// @flow var obj1: { foo: boolean } = { foo: true }; var obj2: { foo: number, bar: boolean, baz: string, } = { foo: 1, bar: true, baz: 'three', };
使用了flow, 对象不能访问不存再的属性, 之前是返回undefined 如今访问报错,若是想知道 访问而且赋值 会不会报错,(我建议你本身试一下)
你可使用 ? 号来标明 这个属性 是可选的,能够为undefined
// @flow var obj: { foo?: boolean } = {}; obj.foo = true; // Works! // $ExpectError obj.foo = 'hello'; // Error!
标明了类型的属性, 他们能够是undefined(属性赋值为undefined) 或者 空着不写(空对象,), 可是他们不能够是null
密封对象的概念不懂的 能够去了解一下, 就是这个对象不能够修改,可是引用的对象仍是能够修改的; 在flow中 这种对象知道全部你声明的属性的值的类型
// @flow var obj = { foo: 1, bar: true, baz: 'three' }; var foo: number = obj.foo; // Works! var bar: boolean = obj.bar; // Works! // $ExpectError var baz: null = obj.baz; // Error! var bat: string = obj.bat; // Error!
并且flow 不容许你往这种对象上面添加新的属性, 否则报错
注意了,flow 是静态类型检测工具 并非动态的, 因此他不能在运行时判断你的变量是什么类型的, 因此他只能判断你这个对象是不是你赋值过的类型之一.
// @flow var obj = {}; if (Math.random()) obj.prop = true; else obj.prop = "hello"; // $ExpectError var val1: boolean = obj.prop; // Error! // $ExpectError var val2: string = obj.prop; // Error! var val3: boolean | string = obj.prop; // Works!
var obj = {}; obj.foo = 1; obj.bar = true; var foo: number = obj.foo; // Works! var bar: boolean = obj.bar; // Works! var baz: string = obj.baz; // Works? // 问题在这里 这里的baz 是不存在的属性, 把他定位string 而且给他一个undefined 是可行的, 可是这部安全, 避免使用
在一个指望正常对象类型的地方,传一个有着额外属性的对象是安全的
// @flow function method(obj: { foo: string }) { // ... } method({ foo: "test", // Works! bar: 42 // Works! });
flow 也支持精确的对象类型, 就是对象不能具备额外的属性;
// @flow var foo: {| foo: string |} = { foo: "Hello", bar: "World!" }; // Error!
若是你想结合精确对象, 须要用到 type 关键字, 我也不知道这个算不算操做符,应该是flow 底层编译支持的, 原声js 并无这个东西
// @flow type FooT = {| foo: string |}; type BarT = {| bar: number |}; type FooBarFailT = FooT & BarT; // 经过这个& 操做能够把两种状况合并,匹配到 foo 和 bar 同时都有的对象 并且类型必须跟声明的同样 type FooBarT = {| ...FooT, ...BarT |}; const fooBarFail: FooBarFailT = { foo: '123', bar: 12 }; // Error! const fooBar: FooBarT = { foo: '123', bar: 12 }; // Works!
虽然有了maps 这个数据结构 可是把对象看成maps 使用 依然很常见
// @flow var o: { [string]: number } = {}; // 制定key值的类型, 能够设置这个类型下的任何key值 o["foo"] = 0; o["bar"] = 1; var foo: number = o["foo"];
索引也是一个可选的名字
// @flow var obj: { [user_id: number]: string } = {}; obj[1] = "Julia"; obj[2] = "Camille"; obj[3] = "Justin"; obj[4] = "Mark";
索引能够和命名属性混合
// @flow var obj: { size: number, [id: number]: string // 此处混合了 索引和命名 } = { size: 0 }; function add(id: number, name: string) { obj[id] = name; obj.size++; }
有时候你想创任意的对象, 那么你就能够传一个空对象,或者一个Object 可是后者是不安全的, 建议避免使用
let arr: Array<number> = [1, 2, 3]; let arr1: Array<boolean> = [true, false, true]; let arr2: Array<string> = ["A", "B", "C"]; let arr3: Array<mixed> = [1, true, "three"]
let arr: number[] = [0, 1, 2, 3]; let arr1: ?number[] = null; // Works! let arr2: ?number[] = [1, 2]; // Works! let arr3: ?number[] = [null]; // Error!
?number[] === ?Array<number>
// @flow let array: Array<number> = [0, 1, 2]; let value: number = array[3]; // Works.// 这里超出了数组的容量
你能够经过下面这样的作法来避免, flow 并未修复这个问题, 因此须要开发者本身注意
let array: Array<number> = [0, 1, 2]; let value: number | void = array[1]; if (value !== undefined) { // number }
这个能够标记一个只能读 不能写的数组
// @flow const readonlyArray: $ReadOnlyArray\<number> = [1, 2, 3]
可是引用类型仍是能够写的
// @flow const readonlyArray: $ReadOnlyArray<{x: number}> = [{x: 1}]; readonlyArray[0] = {x: 42}; // Error! readonlyArray[0].x = 42; // OK
这是一种新的类型, 是一种短的列表,可是时又限制的集合,在 javascript 中这个用数组来声明
// 一个类型对应一个 item let tuple1: [number] = [1]; let tuple2: [number, boolean] = [1, true]; let tuple3: [number, boolean, string] = [1, true, "three"];
能够把取出的值 赋值给具备同样类型的变量, 若是index 超出了索引范围,那么就会返回undefined 在 flow 中也就是 void
// @flow let tuple: [number, boolean, string] = [1, true, "three"]; let num : number = tuple[0]; // Works! let bool : boolean = tuple[1]; // Works! let str : string = tuple[2]; // Works!
若是flow 不知道你要访问的时是那么类型, 那么他忽返回全部可能的类型,
// @flow let tuple: [number, boolean, string] = [1, true, "three"]; function getItem(n: number) { let val: number | boolean | string = tuple[n]; // ... }
javascript 的class 再flow 能够是值 也能够是类型
class MyClass { // ... } let myInstance: MyClass = new MyClass();
class 里的字段必定要声明类型了才能够用
// @flow class MyClass { prop: number;// 若是没有这行, 下的赋值会报错,由于prop 没肯定类型 method() { this.prop = 42; } }
再外部使用的字段,必需要再class 的块里面声明一次
// @flow function func_we_use_everywhere (x: number): number { return x + 1; } class MyClass { static constant: number; // 内部声明 static helper: (number) => number; method: number => number; } MyClass.helper = func_we_use_everywhere MyClass.constant = 42 // 外部使用 MyClass.prototype.method = func_we_use_everywhere
声明而且赋值的语法
class MyClass { prop: number = 42; }
class MyClass<A, B, C> { property: A; method(val: B): C { // ... } }
若是你要把class做为一个类型,你声明了几个泛型, 你就要传几个参数
// @flow class MyClass<A, B, C> { constructor(arg1: A, arg2: B, arg3: C) { // ... } } var val: MyClass<number, boolean, string> = new MyClass(1, true, 'three');
跟上面提到的 type 关键字同样
// @flow type MyObject = { foo: number, bar: boolean, baz: string, };
这个是类型别名 能够在不一样的地方复用
// @flow type MyObject = { // ... }; var val: MyObject = { /* ... */ }; function method(val: MyObject) { /* ... */ } class Foo { constructor(val: MyObject) { /* ... */ } }
type MyObject<A, B, C> = { property: A, method(val: B): C, };
别名泛型是参数化的,也就是你用了之后, 你声明的全部参数 你所有都要传
// @flow type MyObject<A, B, C> = { foo: A, bar: B, baz: C, }; var val: MyObject<number, boolean, string> = { foo: 1, bar: true, baz: 'three', };
经过类型系统的增强抽象
不透明类型别名是不容许访问定义在文件以外的的基础类型的类型别名.
opaque type ID = string; // 一个新的关键字 而且这是声明一个不透明类型别名的语法
不透明类型别名能够复用
// @flow // 在这个例子,我理解的是 外部只能访问到这个文件的ID 类型, 并不能访问到这个文件里面的string 基础类型. 这就是不透明的类型别名 opaque type ID = string; function identity(x: ID): ID { return x; } export type {ID};
你能够可选的加一个子类型约束
在一个 不透明的类型别名
的类型后面
opaque type Alias: SuperType = Type;
任何类型均可以做为父类型 或者 不透明的类型别名
的类型
opaque type StringAlias = string; opaque type ObjectAlias = { property: string, method(): number, }; opaque type UnionAlias = 1 | 2 | 3; opaque type AliasAlias: ObjectAlias = ObjectAlias; opaque type VeryOpaque: AliasAlias = ObjectAlias;
在文件内部跟正常的类型别名同样
//@flow opaque type NumberAlias = number; (0: NumberAlias); function add(x: NumberAlias, y: NumberAlias): NumberAlias { return x + y; } function toNumberAlias(x: number): NumberAlias { return x; } function toNumber(x: NumberAlias): number { return x; }
当你inport 一个 不透明的类型别是时候,他会隐藏基础类型
exports.js
export opaque type NumberAlias = number;
imports.js
import type {NumberAlias} from './exports'; (0: NumberAlias) // Error: 0 is not a NumberAlias! function convert(x: NumberAlias): number { return x; // Error: x is not a number! }
当你添加一个子 类型约束在一个不透明的类型别名上时, 咱们容许不透明类型在被定义文件的外部被用做父类型
exports.js
export opaque type ID: string = string;
imports.js
import type {ID} from './exports'; function formatID(x: ID): string { return "ID: " + x; // Ok! IDs are strings. } function toID(x: string): ID { return x; // Error: strings are not IDs. }
当你建立一个拥有子类型约束的 不透明类型别名, 这个类型在类型中的位置必定要是这个类型的子类型在父类中的位置 (这里的概念应该是跟泛型的概念差很少, 不相关的类型不能够强制转换)
//@flow opaque type Bad: string = number; // Error: number is not a subtype of string opaque type Good: {x: string} = {x: string, y: number};
不透明类型别名 有他们本身的泛型, 可是他们跟正常的泛型是差很少的
// @flow opaque type MyObject<A, B, C>: { foo: A, bar: B } = { foo: A, bar: B, baz: C, }; var val: MyObject<number, boolean, string> = { foo: 1, bar: true, baz: 'three', };
接口可使一些拥有相同方法的类归为一类
// @flow interface Serializable { serialize(): string; } class Foo { serialize() { return '[Foo]'; } } class Bar { serialize() { return '[Bar]'; } } const foo: Serializable = new Foo(); // Works! const bar: Serializable = new Bar(); // Works!
若是你怕出错, 你能够手动的 使用 implements 告诉flow 哪些类实现了哪些接口,这能够预防你修改class 的时候出现错误
// @flow interface Serializable { serialize(): string; } class Foo implements Serializable { serialize() { return '[Foo]'; } // Works! } class Bar implements Serializable { // $ExpectError serialize() { return 42; } // Error! // 不能返回一个number }
不要忘记了接口能够同时实现多个
接口的属性也是能够可选的
interface MyInterface { property?: string; }
接口跟maps 联合
interface MyInterface { [key: string]: number; }
interface MyInterface<A, B, C> { property: A; method(val: B): C; }
规矩还在,泛型你用了几个 ,你使用的时候 就要传递几个参数
// @flow interface MyInterface<A, B, C> { foo: A; bar: B; baz: C; } var val: MyInterface<number, boolean, string> = { foo: 1, bar: true, baz: 'three', };
接口属性默认是不可变
的, 可是你能够添加修饰符让他们变成 covariant
只读或者Contravariance
只写;(关于不可变想了解的请看这里)
interface MyInterface { +covariant: number; // read-only 只读 不能修改 -contravariant: number; // write-only 只能修改, 不能读取 }
混合只读
interface MyInterface { +readOnly: number | string; }
容许指定多个类型
// @flow // $ExpectError interface Invariant { property: number | string } interface Covariant { +readOnly: number | string } var value1: Invariant = { property: 42 }; // Error! var value2: Covariant = { readOnly: 42 }; // Works!
协变(covariant) 属性 一般是只读的,他比正常的属性更有用
// @flow interface Invariant { property: number | string } interface Covariant { +readOnly: number | string } function method1(value: Invariant) { value.property; // Works! value.property = 3.14; // Works! } function method2(value: Covariant) { value.readOnly; // Works! // $ExpectError value.readOnly = 3.14; // Error! }
contravariant
逆变 只写属性 容许你传递更少的类型
// @flow interface Invariant { property: number } interface Contravariant { -writeOnly: number } var numberOrString = Math.random() > 0.5 ? 42 : 'forty-two'; // $ExpectError var value1: Invariant = { property: numberOrString }; // Error! var value2: Contravariant = { writeOnly: numberOrString }; // Works! 能够看到 上面声明了 number 但是这个numberOrString 有两种返回值, 他只能匹配一种 他野是能够传递的
一般比正常的属性更有用
interface Invariant { property: number } interface Contravariant { -writeOnly: number } function method1(value: Invariant) { value.property; // Works! value.property = 3.14; // Works! } function method2(value: Contravariant) { // $ExpectError value.writeOnly; // Error! value.writeOnly = 3.14; // Works! }
类型的值多是不少类型之一
使用 | 分开
Type1 | Type2 | ... | TypeN
能够竖直写
type Foo = | Type1 | Type2 | ... | TypeN
联盟类型能够组合
type Numbers = 1 | 2; type Colors = 'red' | 'blue' type Fish = Numbers | Colors;
当你调用一个要接受联盟类型的函数的时候,你必定要传入一个在联盟类型中的类型,可是在函数里面你要处理全部的类型.
// @flow // $ExpectError function toStringPrimitives(value: number | boolean | string): string { // Error! if (typeof value === 'number') { return String(value); } else if (typeof value === 'boolean') { return String(value); } // 注意这个函数会报错是由于 你用了if 条件语句 并无在全部的状况中返回值, 若是返回了undefined 那么就不符合 string 类型,因此就报错了 }
这里是上面演示的说明,可使用 typeof 关键字来应对逐一的类型
// @flow function toStringPrimitives(value: number | boolean | string) { if (typeof value === 'number') { return value.toLocaleString([], { maximumSignificantDigits: 3 }); // Works! } // ... }
概念就不说了,难懂来看一下例子
想象咱们有一个处理发送了请求以后响应的函数,当请求成功你那个的时候,咱们获得一个对象,这个对象有 一个 success 属性 值为true 还有一个值咱们须要更新的值, value
{ success: true, value: false };
当请求失败的时候,咱们获得一个对象这个对象有一个 success 属性 值为false,和一个 error 属性,定义了一个错误.
{ success: false, error: 'Bad request' };
咱们能够尝试用一个对象去描述这两个对象, 然而咱们很快就发生了一个问题, 就是咱们知道一个属性的存在与否(value 或者 error ) 取决于success(由于success为 true 那么 value才会存在) 可是 Flow 不知道.
// @flow type Response = { success: boolean, value?: boolean, error?: string }; function handleResponse(response: Response) { if (response.success) { // $ExpectError var value: boolean = response.value; // Error! } else { // $ExpectError var error: string = response.error; // Error! } }
取而代之,若是咱们建立一个两个对象类型的联盟类型,Flow 会知道基于success 属性 咱们会使用哪一个对象
// @flow type Success = { success: true, value: boolean }; type Failed = { success: false, error: string }; type Response = Success | Failed; (这就是脱节联盟) function handleResponse(response: Response) { if (response.success) { var value: boolean = response.value; // Works! } else { var error: string = response.error; // Works! } }
脱节连门要求你使用单一的属性去区分每一个对象类型,你不能用两个不一样的属性,去区分两个不一样的类型
// @flow type Success = { success: true, value: boolean }; type Failed = { error: true, message: string }; function handleResponse(response: Success | Failed) { if (response.success) { // $ExpectError var value: boolean = response.value; // Error! } } // 不懂的跟上面的对比一下, 两个对象必需要有一个属性是相同的
然而 你能够用精确对象类型
// @flow type Success = {| success: true, value: boolean |}; type Failed = {| error: true, message: string |}; // 精确的也就是说 不能够扩展对象, 他该是哪一个就是哪一个 不存在混乱 type Response = Success | Failed; function handleResponse(response: Response) { if (response.success) { var value: boolean = response.value; } else { var message: string = response.message; } }
全部不一样类型的类型值
// @flow type A = { a: number }; type B = { b: boolean }; type C = { c: string }; function method(value: A & B & C) { // ... } // $ExpectError method({ a: 1 }); // Error! // $ExpectError method({ a: 1, b: true }); // Error! method({ a: 1, b: true, c: 'three' }); // Works!
能够把上面的连门类型理解为或 把交叉类型理解为& 语法都是同样的
type Foo = & Type1 & Type2 & ... & TypeN type Foo = Type1 & Type2; type Bar = Type3 & Type4; type Baz = Foo & Bar;
咱们在函数中和联盟函数相反, 咱们不如传入全部的类型,可是在函数里面咱们只须要作处理一种状况就OK
// @flow type A = { a: number }; type B = { b: boolean }; type C = { c: string }; function method(value: A & B & C) { var a: A = value; var b: B = value; var c: C = value; }
你总不能一个值 是数字的同时又是字符串吧
// @flow type NumberAndString = number & string; function method(value: NumberAndString) { // ... } // $ExpectError method(3.14); // Error! // $ExpectError method('hi'); // Error!
当你建立一个交叉对象类型时,你是在合并了他们全部的属性在一个对象上
// @flow type One = { foo: number }; type Two = { bar: boolean }; type Both = One & Two; var value: Both = { foo: 1, bar: true };
若是声明的属性类型相同, 就至关于你声明了一个 交叉类型的属性
js有一个typeof 关键字,他会返回一个字符串说明
然而他是有限制的,typeof 对象 数组 null 都是 object
因此在flow中, 他把这个关键字重载了
// @flow let num1 = 42; let num2: typeof num1 = 3.14; // Works! // $ExpectError let num3: typeof num1 = 'world'; // Error! let bool1 = true; let bool2: typeof bool1 = false; // Works! // $ExpectError let bool3: typeof bool1 = 42; // Error! let str1 = 'hello'; let str2: typeof str1 = 'world'; // Works! // $ExpectError let str3: typeof str1 = false; // Error!
你能够typeof 任何值
// @flow let obj1 = { foo: 1, bar: true, baz: 'three' }; let obj2: typeof obj1 = { foo: 42, bar: false, baz: 'hello' }; let arr1 = [1, 2, 3]; let arr2: typeof arr1 = [3, 2, 1];
你能够用typeof 的返回值做为一个类型
可是若是你typeof 一个指定了字面量类型的 变量, 那么那个类型就是字面量的值了
// @flow let num1: 42 = 42; // $ExpectError let num2: typeof num1 = 3.14; // Error! // 看这里 num1 的type 指定了是 42 那么 typeof num1 不会返回number 会返回 42 因此3.14 不符合 let bool1: true = true; // $ExpectError let bool2: typeof bool1 = false; // Error! let str1: 'hello' = 'hello'; // $ExpectError let str2: typeof str1 = 'world'; // Error!
// @flow class MyClass { method(val: number) { /* ... */ } } class YourClass { method(val: number) { /* ... */ } } // $ExpectError let test1: typeof MyClass = YourClass; // Error! let test2: typeof MyClass = MyClass; // Works! // 看这里 es6 的类并非一种类型, 只是一种语法糖而已,内部机制仍是原型链, 因此 typeof MyClass 不会等于YourClass
把一个值镶嵌到不一样的类型
有时不使用函数和变量去声明一个类型是颇有用的,因此flow 支持多种方式去干这个事情(声明一个类型)
(value: Type)
这个表达式能够出如今表达式能出现的任何地方
let val = (value: Type); let obj = { prop: (value: Type) }; let arr = ([(value: Type), (value: Type)]: Array<Type>);
也能够这样写
(2 + 2: number);
// @flow let value = 42; // 这个的做用就是把变量 嵌入到一个类型中去 (value: 42); // Works! (value: number); // Works! (value: string); // Error!
这个表达式是由返回值的,若是你接收了这个返回值,你会获得一个新的类型
// @flow let value = 42; (value: 42); // Works! (value: number); // Works! let newValue = (value: number); // $ExpectError (newValue: 42); // Error! (newValue: number); // Works!
let value = 42; (value: number); // Works! // $ExpectError (value: string); // Error! // 这里先把value 变成any 再变成string let newValue = ((value: any): string); // $ExpectError (newValue: number); // Error! // 生效了 (newValue: string); // Works!
可是合适不安全且不推荐的,可是有时候他颇有用
如果你想检查一个对象的类型,你不能直接 typeof 你得先用 断言表达式去转换而后再用typeof 去检查
想这样:
function clone(obj: { [key: string]: mixed }) { const cloneobj = {}; Object.keys(obj).forEach(key => { cloneobj[key] = obj[key]; }); return ((cloneobj: any): typeof obj); } const obj = clone({foo: 1}) (obj.foo: 1) // 出错!
function clone(obj) { (obj: { [key: string]: mixed }); const cloneobj = {}; Object.keys(obj).forEach(key => { cloneobj[key] = obj[key]; }); return ((cloneobj: any): typeof obj); } const obj = clone({foo: 1}) (obj.foo: 1) // ok!
flow 提供了一系列的 工具类型, 以便于再一些常见场景使用
详情看这里
上面由相似的, 就是一个export 一个 import
感受没多大用处, 能够作一些标记,这个能够在不经过flow 编译的状况下直接使用在js文件上
// @flow /*:: type MyAlias = { foo: number, bar: boolean, baz: string, }; */ function method(value /*: MyAlias */) /*: boolean */ { return value.bar; } method({ foo: 1, bar: true, baz: ["oops"] });
看完能看懂全部flow 代码了吧...