遍尋百度,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 代碼了吧...