摘要:起因遍尋百度沒發(fā)現(xiàn)的中文文檔這對國內(nèi)顯然是不友好的雖說平時用不著但是一般框架都會用一下以便用戶可以準(zhǔn)確的使用框架可以避免很多謎一樣的既然沒有那我就來翻譯一下咯計劃先翻譯類型注釋部分安裝的一搜一大把類型注釋當(dāng)你的類型不注釋的時候就不起作用了來
起因
遍尋百度,google,沒發(fā)現(xiàn)flow的中文文檔,這對國內(nèi)顯然是不友好的,雖說flow 平時用不著, 但是一般框架都會用一下,以便用戶可以準(zhǔn)確的使用框架,可以避免很多謎一樣的BUG,既然沒有,那我就來翻譯一下咯.計劃先翻譯類型注釋(types annotations)部分,安裝的一搜一大把.
flow 類型注釋當(dāng)你的類型不注釋的時候, flow 就不起作用了,so 來看看 flow 類型 可以如何注釋. 可不是 // 這個注釋
原始類型javascript 一共有6鐘原始數(shù)據(jù)類型.
Booleans
Strings
Numbers
null
undefined (void in Flow types)
Symbols (new in ECMAScript 2015, not yet supported in Flow) flow 不支持symbols
原始類型分兩種,一種是字面量的, 一種是包裝過的 比如 3 跟 Number(3);
比如下面這樣,你只能傳 字面量.booleans 除外
// @flow function method(x: number, y: string, z: boolean) { // ... } method(3.14, "hello", true);booleans
flow 可以識別 !!x 和 Boolean(0) 的明確類型轉(zhuǎn)換的boolean
// @flow function acceptsBoolean(value: boolean) { // ... } acceptsBoolean(0); // Error! 錯誤 acceptsBoolean(Boolean(0)); // Works! OK acceptsBoolean(!!0); // Works! OKnumber
數(shù)字就很明確了
// @flow function acceptsNumber(value: number) { // ... } acceptsNumber(42); // Works! acceptsNumber(3.14); // Works! acceptsNumber(NaN); // Works! acceptsNumber(Infinity); // Works! acceptsNumber("foo"); // Error!string
// @flow function acceptsString(value: string) { // ... } acceptsString("foo"); // Works! acceptsString(false); // Error!
js中 字符串會有隱藏的轉(zhuǎn)換
"foo" + 42; // "foo42" "foo" + {}; // "foo[object Object]"
flow 只支持?jǐn)?shù)字和字符串的隱藏轉(zhuǎn)換
// @flow "foo" + "foo"; // Works! "foo" + 42; // Works! "foo" + {}; // Error! "foo" + []; // Error!
如果你要使用, 必須要明確轉(zhuǎn)換
// @flow "foo" + String({}); // Works! "foo" + [].toString(); // Works! "" + JSON.stringify({}) // Works!null 和 undefined
在flow中 undefined 是 void
// @flow function acceptsNull(value: null) { /* ... */ } function acceptsUndefined(value: void) { /* ... */ } acceptsNull(null); // Works! acceptsNull(undefined); // Error! acceptsUndefined(null); // Error! acceptsUndefined(undefined); // Works!可能的類型
可能的類型, 是要用再 那些可選的值, 你可以使用一個問號來標(biāo)記他, 證明這個值是可選的,并不是必須的
// @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
函數(shù)參數(shù)的選項加個問號標(biāo)明,這個函數(shù)的參數(shù)可可選的
// @flow function acceptsOptionalString(value?: string) { // ... } acceptsOptionalString("bar"); // Works! acceptsOptionalString(undefined); // Works! acceptsOptionalString(null); // Error! acceptsOptionalString(); // Works!函數(shù)的默認(rèn)參數(shù)
es5 的新特性
// @flow function acceptsOptionalString(value: string = "foo") { // ... } acceptsOptionalString("bar"); // Works! acceptsOptionalString(undefined); // Works! acceptsOptionalString(null); // Error! acceptsOptionalString(); // Works!symbol
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!雜交類型 (mixed types)
你可以匹配多個類型
function stringifyBasicValue(value: string | number) { return "" + value; }
你可以像java 的泛型一樣(有區(qū)別)去標(biāo)明一個類型,下面的例子 標(biāo)明,該函數(shù)返回的類型跟傳進函數(shù)的類型相同.
function identity(value: T): T { return value; }
你可以這樣來 標(biāo)記 一個函數(shù)可以接受任何類型的參數(shù)
function getTypeOf(value: mixed): string { return typeof value; }
當(dāng)你使用 mixed 時候, 雖然你可以傳進任何類型, 但是你返回的時候 必須要明確他是什么類型, 不然就報錯
// @flow function stringify(value: mixed) { // $ExpectError return "" + value; // Error! } stringify("foo");任何類型(any type)
不要搞混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
舊代碼 新增flow 類型檢查,并且 用其他類型的會引起大量錯誤
當(dāng)你明確的知道你的代碼不能通過類型檢查的時候,
避免泄漏any當(dāng)你聲明了 傳進的參數(shù)的any的時候,那么你返回的參數(shù)也都是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;可能類型 (maybe type)
就是上面提到的 可以用 ? 問號標(biāo)記他是可選的類型
變量類型 (variable type)var - 聲明一個變量,選擇性賦值
let - 聲明一個塊級變量,選擇性輔助
const - 聲明一個塊級變量,并賦值,且不能再次賦值
在flow 分為兩組, 一組是 let 和 var 可以再次賦值, 另一組是const 不能再次賦值
constconst 可以注入你賦值的類型, 或者你自己手動的指定類型
// @flow const foo /* : number */ = 1; const bar: number = 2;let 和 var
跟上面一樣, 這兩個也可以自動的注入類型
但是 若你自動注入的類型, 你重新賦值修改的類型的時候并不會得到報錯
如果語句,函數(shù),和其他的條件代碼,可以精確的指出他是什么類型,那么就可以避免flow 的檢查 不然就報錯
// @flow let foo = 42; function mutate() { foo = true; foo = "hello"; } mutate(); // $ExpectError let isString: string = foo; // Error!
盡量避免上面的用法(個人推薦)
函數(shù)類型(function type)函數(shù)只有兩種用法, 要么參數(shù), 要么返回值
// @flow function concat(a: string, b: string): string { return a + b; } concat("foo", "bar"); // Works! // $ExpectError concat(true, false); // Error!聲明函數(shù)
同上
箭頭函數(shù)(str: string, bool?: boolean, ...nums: Array帶回調(diào)的箭頭函數(shù)) => void
function method(callback: (error: Error | null, value: string | null) => void) { // 上面表明, 這和函數(shù)接受的參數(shù) 只能是 error null 和 string 然后染回一個 undefined }可選參數(shù)
// @flow function method(optionalValue?: string) { // ... } method(); // Works. method(undefined); // Works. method("string"); // Works. // $ExpectError method(null); // Error!剩余參數(shù)的用法 (rest parameter)
function method(...args: Array函數(shù)返回值) { // ... 使類似java 泛型的 樣子 來聲明他的類型 }
function method(): number { // 若是標(biāo)明了返回類型, 那么你的函數(shù)一定要有返回值,如果有條件判斷語句,那么每個條件都要有返回值 }函數(shù)的this
你不用注釋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 問題出現(xiàn)再truthy 上 可能是 經(jīng)過了隱式的類型轉(zhuǎn)換 return a + b; } return ""; }
你可以這樣來修復(fù)上面的問題, 再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 再函數(shù)上你還可以用 Function 不過這是不穩(wěn)定的,你應(yīng)該避免使用他
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 現(xiàn)在訪問報錯,如果想知道 訪問并且賦值 會不會報錯,(我建議你自己試一下)
你可以使用 ? 號來標(biāo)明 這個屬性 是可選的,可以為undefined
// @flow var obj: { foo?: boolean } = {}; obj.foo = true; // Works! // $ExpectError obj.foo = "hello"; // Error!
標(biāo)明了類型的屬性, 他們可以是undefined(屬性賦值為undefined) 或者 空著不寫(空對象,), 但是他們不可以是null
密封對象 (seald objects)密封對象的概念不懂的 可以去了解一下, 就是這個對象不可以修改,但是引用的對象還是可以修改的; 在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 是靜態(tài)類型檢測工具 并不是動態(tài)的, 所以他不能在運行時判斷你的變量是什么類型的, 所以他只能判斷你這個對象是否是你賦值過的類型之一.
// @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 是可行的, 但是這部安全, 避免使用額外對象類型 (extra object type)
在一個期望正常對象類型的地方,傳一個有著額外屬性的對象是安全的
// @flow function method(obj: { foo: string }) { // ... } method({ foo: "test", // Works! bar: 42 // Works! });
flow 也支持精確的對象類型, 就是對象不能具有額外的屬性;
// @flow var foo: {| foo: string |} = { foo: "Hello", bar: "World!" }; // Error!
如果你想結(jié)合精確對象, 需要用到 type 關(guān)鍵字, 我也不知道這個算不算操作符,應(yīng)該是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 (objects as maps)
雖然有了maps 這個數(shù)據(jù)結(jié)構(gòu) 但是把對象當(dāng)作maps 使用 依然很常見
// @flow var o: { [string]: number } = {}; // 制定key值的類型, 可以設(shè)置這個類型下的任何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 Type)
有時候你想創(chuàng)任意的對象, 那么你就可以傳一個空對象,或者一個Object 但是后者是不安全的, 建議避免使用
數(shù)組類型let arr: Array簡寫= [1, 2, 3]; let arr1: Array = [true, false, true]; let arr2: Array = ["A", "B", "C"]; let arr3: Array = [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
// @flow let array: Array= [0, 1, 2]; let value: number = array[3]; // Works.// 這里超出了數(shù)組的容量
你可以通過下面這樣的做法來避免, flow 并未修復(fù)這個問題, 所以需要開發(fā)者自己注意
let array: Array$ReadOnlyArray= [0, 1, 2]; let value: number | void = array[1]; if (value !== undefined) { // number }
這個可以標(biāo)記一個只能讀 不能寫的數(shù)組
// @flow const readonlyArray: $ReadOnlyArray= [1, 2, 3]
但是引用類型還是可以寫的
// @flow const readonlyArray: $ReadOnlyArray<{x: number}> = [{x: 1}]; readonlyArray[0] = {x: 42}; // Error! readonlyArray[0].x = 42; // OKtuple types
這是一種新的類型, 是一種短的列表,但是時又限制的集合,在 javascript 中這個用數(shù)組來聲明
// 一個類型對應(yīng)一個 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]; // ... }tuple類型的長度一定要嚴(yán)格等于你聲明時候的長度 tuple 不能匹配 數(shù)組類型, 這也是他們的差別 tuple 只能用 array 的 join() 方法 其他的都不可以用,否則報錯 class type
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; // 內(nèi)部聲明 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 { property: A; method(val: B): C { // ... } }
如果你要把class作為一個類型,你聲明了幾個泛型, 你就要傳幾個參數(shù)
// @flow class MyClass { constructor(arg1: A, arg2: B, arg3: C) { // ... } } var val: MyClass別名類型(type aliases)= new MyClass(1, true, "three");
跟上面提到的 type 關(guān)鍵字一樣
// @flow type MyObject = { foo: number, bar: boolean, baz: string, };
這個是類型別名 可以在不同的地方復(fù)用
// @flow type MyObject = { // ... }; var val: MyObject = { /* ... */ }; function method(val: MyObject) { /* ... */ } class Foo { constructor(val: MyObject) { /* ... */ } }別名泛型
type MyObject = { property: A, method(val: B): C, };
別名泛型是參數(shù)化的,也就是你用了以后, 你聲明的所有參數(shù) 你全部都要傳
// @flow type MyObject = { foo: A, bar: B, baz: C, }; var val: MyObject不透明的類型別名(opaque type aliases)= { foo: 1, bar: true, baz: "three", };
通過類型系統(tǒng)的加強抽象
不透明類型別名是不允許訪問定義在文件之外的的基礎(chǔ)類型的類型別名.
opaque type ID = string; // 一個新的關(guān)鍵字 并且這是聲明一個不透明類型別名的語法
不透明類型別名可以復(fù)用
// @flow // 在這個例子,我理解的是 外部只能訪問到這個文件的ID 類型, 并不能訪問到這個文件里面的string 基礎(chǔ)類型. 這就是不透明的類型別名 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;不透明別名類型 的類型檢查 在文件內(nèi)部
在文件內(nèi)部跟正常的類型別名一樣
//@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; }在文件外部
當(dāng)你inport 一個 不透明的類型別是時候,他會隱藏基礎(chǔ)類型
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! }子類型約束(subTyping Constraints)
當(dāng)你添加一個子 類型約束在一個不透明的類型別名上時, 我們允許不透明類型在被定義文件的外部被用作父類型
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. }
當(dāng)你創(chuàng)建一個擁有子類型約束的 不透明類型別名, 這個類型在類型中的位置一定要是這個類型的子類型在父類中的位置 (這里的概念應(yīng)該是跟泛型的概念差不多, 不相關(guān)的類型不可以強制轉(zhuǎn)換)
//@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: { foo: A, bar: B } = { foo: A, bar: B, baz: C, }; var val: MyObject接口類型 (interface Types)= { 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 哪些類實現(xiàn)了哪些接口,這可以預(yù)防你修改class 的時候出現(xiàn)錯誤
// @flow interface Serializable { serialize(): string; } class Foo implements Serializable { serialize() { return "[Foo]"; } // Works! } class Bar implements Serializable { // $ExpectError serialize() { return 42; } // Error! // 不能返回一個number }
不要忘記了接口可以同時實現(xiàn)多個
接口的屬性也是可以可選的
interface MyInterface { property?: string; }
接口跟maps 聯(lián)合
interface MyInterface { [key: string]: number; }接口泛型
interface MyInterface { property: A; method(val: B): C; }
規(guī)矩還在,泛型你用了幾個 ,你使用的時候 就要傳遞幾個參數(shù)
// @flow interface MyInterface { foo: A; bar: B; baz: C; } var val: MyInterface接口屬性的 只讀,與只寫= { foo: 1, bar: true, baz: "three", };
接口屬性默認(rèn)是不可變的, 但是你可以添加修飾符讓他們變成 covariant只讀或者Contravariance 只寫;(關(guān)于不可變想了解的請看這里)
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!
協(xié)變(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! }聯(lián)盟類型 (union types)
類型的值可能是很多類型之一
使用 | 分開
Type1 | Type2 | ... | TypeN
可以豎直寫
type Foo = | Type1 | Type2 | ... | TypeN
聯(lián)盟類型可以組合
type Numbers = 1 | 2; type Colors = "red" | "blue" type Fish = Numbers | Colors;聯(lián)盟類型請求一個,但是所有的都要處理
當(dāng)你調(diào)用一個要接受聯(lián)盟類型的函數(shù)的時候,你一定要傳入一個在聯(lián)盟類型中的類型,但是在函數(shù)里面你要處理所有的類型.
// @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); } // 注意這個函數(shù)會報錯是因為 你用了if 條件語句 并沒有在所有的情況中返回值, 如果返回了undefined 那么就不符合 string 類型,所以就報錯了 }聯(lián)盟改進
這里是上面演示的說明,可以使用 typeof 關(guān)鍵字來應(yīng)對逐一的類型
// @flow function toStringPrimitives(value: number | boolean | string) { if (typeof value === "number") { return value.toLocaleString([], { maximumSignificantDigits: 3 }); // Works! } // ... }脫節(jié)聯(lián)盟 (disjoint Unions)
概念就不說了,難懂來看一下例子
想象我們有一個處理發(fā)送了請求之后響應(yīng)的函數(shù),當(dāng)請求成功你那個的時候,我們得到一個對象,這個對象有 一個 success 屬性 值為true 還有一個值我們需要更新的值, value
{ success: true, value: false };
當(dāng)請求失敗的時候,我們得到一個對象這個對象有一個 success 屬性 值為false,和一個 error 屬性,定義了一個錯誤.
{ success: false, error: "Bad request" };
我們可以嘗試用一個對象去描述這兩個對象, 然而我們很快就發(fā)生了一個問題, 就是我們知道一個屬性的存在與否(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! } }
取而代之,如果我們創(chuàng)建一個兩個對象類型的聯(lián)盟類型,Flow 會知道基于success 屬性 我們會使用哪個對象
// @flow type Success = { success: true, value: boolean }; type Failed = { success: false, error: string }; type Response = Success | Failed; (這就是脫節(jié)聯(lián)盟) function handleResponse(response: Response) { if (response.success) { var value: boolean = response.value; // Works! } else { var error: string = response.error; // Works! } }脫節(jié)聯(lián)盟與精確類型儀器使用
脫節(jié)連門要求你使用單一的屬性去區(qū)分每個對象類型,你不能用兩個不同的屬性,去區(qū)分兩個不同的類型
// @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; } }交叉類型(intersection types)
所有不同類型的類型值
// @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;
我們在函數(shù)中和聯(lián)盟函數(shù)相反, 我們不如傳入所有的類型,但是在函數(shù)里面我們只需要做處理一種情況就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; }不可能的交叉類型
你總不能一個值 是數(shù)字的同時又是字符串吧
// @flow type NumberAndString = number & string; function method(value: NumberAndString) { // ... } // $ExpectError method(3.14); // Error! // $ExpectError method("hi"); // Error!交叉對象類型
當(dāng)你創(chuàng)建一個交叉對象類型時,你是在合并了他們所有的屬性在一個對象上
// @flow type One = { foo: number }; type Two = { bar: boolean }; type Both = One & Two; var value: Both = { foo: 1, bar: true };
如果聲明的屬性類型相同, 就相當(dāng)于你聲明了一個 交叉類型的屬性
typeof Types (這個不好翻譯 因為 typeof 是js中的一個關(guān)鍵字,在此我就不翻譯這個了)js有一個typeof 關(guān)鍵字,他會返回一個字符串說明
然而他是有限制的,typeof 對象 數(shù)組 null 都是 object
所以在flow中, 他把這個關(guān)鍵字重載了
// @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 的返回值作為一個類型
但是如果你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!其他類型的 typeof 繼承行為
// @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 的類并不是一種類型, 只是一種語法糖而已,內(nèi)部機制還是原型鏈, 所以 typeof MyClass 不會等于YourClass鑲嵌表達式類型(type casting expression)
把一個值鑲嵌到不同的類型
有時不使用函數(shù)和變量去聲明一個類型是很有用的,所以flow 支持多種方式去干這個事情(聲明一個類型)
語法(value: Type)
這個表達式可以出現(xiàn)在表達式能出現(xiàn)的任何地方
let val = (value: Type); let obj = { prop: (value: Type) }; let arr = ([(value: Type), (value: Type)]: Array);
也可以這樣寫
(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!通過 any 去轉(zhuǎn)換類型
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 你得先用 斷言表達式去轉(zhuǎn)換然后再用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
注釋類型感覺沒多大用處, 可以做一些標(biāo)記,這個可以在不通過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 代碼了吧...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/97778.html
摘要:介紹是個的靜態(tài)類型檢查工具,由出品的開源碼項目,問世只有一年多,是個相當(dāng)年輕的項目。現(xiàn)在,提供了另一個新的選項,它是一種強靜態(tài)類型的輔助檢查工具。 showImg(https://segmentfault.com/img/bVH6mL?w=1200&h=675); 本章的目標(biāo)是提供一些Flow工具的介紹與使用建議。Flow本質(zhì)上也只是個檢查工具,它并不會自動修正代碼中的錯誤,也不會強制...
摘要:原文鏈接翻譯于今天我們興奮的發(fā)布了的嘗鮮版,一個新的靜態(tài)類型檢查器。為添加了靜態(tài)類型檢查,以提高開發(fā)效率和代碼質(zhì)量。這最終形成一個高度并行增量式的檢查架構(gòu),類似。知道縮小類型范圍時做動態(tài)檢查的影響。 原文鏈接:https://code.facebook.com/posts/1505962329687926/flow-a-new-static-type-checker-for-java...
摘要:運行時用來創(chuàng)建實例渲染并處理虛擬等的代碼。基本上就是除去編譯器的其它一切。版本可以通過標(biāo)簽直接用在瀏覽器中。為這些打包工具提供的默認(rèn)文件是只有運行時的構(gòu)建。為瀏覽器提供的用于在現(xiàn)代瀏覽器中通過直接導(dǎo)入。 Vue版本:2.6.9 源碼結(jié)構(gòu)圖 ├─ .circleci // 包含CircleCI持續(xù)集成/持續(xù)部署工具的配置文件 ├─ .github ...
摘要:運行時用來創(chuàng)建實例渲染并處理虛擬等的代碼。基本上就是除去編譯器的其它一切。版本可以通過標(biāo)簽直接用在瀏覽器中。為這些打包工具提供的默認(rèn)文件是只有運行時的構(gòu)建。為瀏覽器提供的用于在現(xiàn)代瀏覽器中通過直接導(dǎo)入。 Vue版本:2.6.9 源碼結(jié)構(gòu)圖 ├─ .circleci // 包含CircleCI持續(xù)集成/持續(xù)部署工具的配置文件 ├─ .github ...
閱讀 2579·2023-04-26 03:00
閱讀 1392·2021-10-12 10:12
閱讀 4190·2021-09-22 15:33
閱讀 2908·2021-09-22 15:06
閱讀 1530·2019-08-30 15:44
閱讀 2145·2019-08-30 13:59
閱讀 534·2019-08-30 11:24
閱讀 2407·2019-08-29 17:07