Flow入門初識 Flow是facebook出品的JavaScript靜態類型檢查工具。 由於JavaScript是動態類型語言,它的靈活性也會造成一些代碼隱患,使用Flow可以在編譯期儘早發現由類型錯誤引起的bug,這種方式非常有利於大型項目源碼的開發和維護。 一、 Flow的安裝 創建一個項目文 ...
Flow入門初識
Flow是facebook出品的JavaScript靜態類型檢查工具。
由於JavaScript是動態類型語言,它的靈活性也會造成一些代碼隱患,使用Flow可以在編譯期儘早發現由類型錯誤引起的bug,這種方式非常有利於大型項目源碼的開發和維護。
一、 Flow的安裝
npm install --g flow-bin
創建一個項目文件夾./demo
進入項目文件夾。
npm init -y
創建package.json文件,在文件中的scripts中添加:
"scripts": {
"flow": "flow"
}
這就完成瞭然後開始正式使用。
二、正式學習
1、通過npm run flow init
命令會在項目文件夾的根目錄創建一個.flowconfig
文件。
2、通過npm run flow check
命令可以在你的項目根目錄以及任何子目錄文件夾下進行專門的類型檢查,但是,這並不是最高效的使用方式,因為每次Flow都會重新檢查整個項目的所有文件,開發過程中,推薦啟動Flow服務。
3、通過npm run flow
命令啟動Flow服務,Flow服務的工作方式是增量檢查也就是說它只檢查變化的部分,首次運行該命令時,服務啟動並且顯示最初類型檢查結果,這保證了Flow更高效的增量式工作流,然後接下來每次想要知道檢測結果,只要輸入flow
命令即可。開髮結束之後,輸入npm run flow stop
停止服務。
Flow的類型檢查是可選的,並不需要一次性檢查所有代碼。你可以選擇你想要檢查的文件,只要在對應的JavaScript文件最前面加上帶有@flow
標識的註釋即可:
/*@flow*/
function foo(a) {
return a;
}
fn(1);
三、類型推斷
通常,類型檢查分為以下兩種方式:
1、通過註釋:事先註釋好我們期待的類型,Flow就會基於這些註釋來評估
2、通過代碼推斷:通過變數的使用上下文來推斷出變數類型,然後根據這些推斷來檢查類型
第一種方式,我們需要額外編寫只在開發階段起作用的代碼,最後在代碼編譯打包的階段被剔除。顯然,這種額外添加類型註釋的方式增加了工作量。
第二種方式,不需要任何代碼修改即可進行類型檢查,最小化開發者的工作量。它不會強制你改變開發習慣,因為它會自動推斷出變數的類型。這就是所謂的類型推斷,Flow最重要的特性之一。
/*@flow*/
function foo(x) {
return x.split(' ');
}
foo(34);
當你在終端運行npm run flow
命令的時候,上述代碼會報錯,因為函數foo()
的期待參數是字元串,而我們輸入了數字,錯誤信息類似如下:
上述信息清楚地指出了出錯位置和錯誤原因。我們只要將參數變成字元串,即可修正錯誤,如下所示:
/*@flow*/
function foo(x) {
return x.split(' ');
}
foo("Hello World");
split()
方法只適用於string
類型的變數,所以x
應該是string
,這就是類型推斷。
四、空類型
Flow處理null
。它不會忽略null
,這樣可以防止,因為給變數傳了null
而導致程式崩潰的錯誤。
/*@flow*/
function stringLength(str) {
return str.length;
}
var length = stringLength(null);
Flow會報錯。為了防止出錯,我們需要單獨處理null
。
/*@flow*/
function stringLength (str) {
if (str !== null) {
return str.length;
}
return 0;
}
var length = stringLength(null);
代碼中我們引入對null
的檢查,確保代碼能在任何情況下都正常且正確運行。上述代碼可以通過Flow的類型檢查。
五、類型註釋
類型推斷是Flow最有用的特性之一,不需要編寫類型註釋就能獲取有用的反饋。但在某些特定的場景下,添加類型註釋可以提供更好更明確的檢查依據。
/*@flow*/
function foo(x, y){
return x + y;
}
foo('Hello', 18);
Flow檢查上述代碼時檢查不出任何錯誤,因為+
即可以用在字元串上,也可以用在數字上,我們並沒有明確指出foo()
的參數必須為數字。
在這種情況下,我們可以藉助類型註釋來指明期望的類型。類型註釋是以冒號:
開頭,可以在函數參數,返回值,變數聲明中使用,如果我們在上段代碼中添加類型註釋,就會變成如下:
/*@flow*/
function foo(x : number, y : number) : number {
return x + y;
}
foo('Hello', 18);
第一個和第二個number是x和y兩個形參需要接收number類型的值,第三個number是foo()函數需要返回一個number的值
現在Flow就能檢查出錯誤,因為函數參數的期待類型為數字,而我們提供了字元串,錯誤信息:
如果傳入的參數是數字,就不會有錯誤。
1、函數的類型註釋
/*@flow*/
function add(x : number, y : number) : number {
return x + y;
}
add(3, 4);
上述代碼展示了變數類型註釋以及函數類型註釋。函數add()
的參數,以及函數的返回值,期待類型為數字。如果傳入其他類型參數,Flow就會檢測到錯誤。
2、數組的類型註釋
/*@flow*/
var foo : Array<number> = [1,2,3];
數組類型註釋的格式是Array<T>
,T
表示數組中每項的數據類型。在上述代碼中,foo
是每項均為數字的數組。
3、類的類型註釋
下麵展示了類和對象的類型註釋模型。唯一需要註意的是,可以在兩個類型之間使用或邏輯,用|
來間隔。變數bar1
添加了必須為Bar
類的類型註釋。
/*@flow*/
class Bar {
x: string;
y: string | number;
constructor(x, y) {
this.x = x;
this.y = y;
}
}
var bar1: Bar = new Bar("hello", 4);
4、對象字面量的類型註釋
對象的類型註釋,跟類的類型註釋很像,指定對象屬性的類型。
/*@flow*/
var obj: { a: string, b: number, c: Array<string>, d: Bar } = {
a: "hello",
b: 42,
c: ["hello", "world"],
d: new Bar("hello", 3)
}
5、null的類型註釋
若想任意類型T
可以為null
或者undefined
,只需類似如下寫成?T
的格式即可。
/*@flow*/
var foo: ?string = null;
此時,foo
可以為字元串,也可以為null
。
六、庫的定義
我們經常需要引入第三方庫,Flow檢查時就會拋出錯誤。但這並不是我們期待的錯誤。
慶幸的是,我們不需要修改庫源碼去防止這些報錯。我們只需創建一個庫定義(libdef)。libdef是包含第三方庫聲明的JS文件簡稱。
觀察下麵的例子:
/* @flow */
var users = [
{ name: 'John', designation: 'developer' },
{ name: 'Doe', designation: 'designer' }
];
function getDeveloper() {
return _.findWhere(users, {designation: 'developer'});
}
會報錯:
由於Flow並不認識$
,所以會報錯。要解決這個問題,我們需要引入jQuery的庫定義。
使用flow-typed
通過npm install -g flow-typed
安裝flow-typed倉庫,它包含了眾多流行的第三方庫的libdef。只需在項目根目錄下創建一個名為flow-typed
的文件夾,並且下載相關的定義文件即可。
安裝成功之後, 運行flow-typed install
來檢查package.json
文件,並且下載所有項目中用到的第三方庫的libdef。
等待的時間有點久,等下載完後,再npm run flow
,就會發現沒有錯誤了。
自定義libdef
如果你用的庫並不在flow-typed倉庫,你可以創建你自己的libdef,感興趣可以查看自定義libdef;
七、剔除類型註釋
由於額外添加的類型註釋不是正確的JavaScript語法,打包編譯的時候需要在源碼中剔除。可以通過flow-remove-types來剔除,或者如果你已經用Babel來轉譯JS,你可以使用Babel preset來移除。我們只討論第一種方法。
首先需要安裝flow-remove-types作為項目依賴庫:npm install --save-dev flow-remove-types
然後在package.json
文件中添加另一個script
入口:
"scripts": {
"flow": "flow",
"build": "flow-remove-types src/ -d dist/"
}
運行npm run build
將剔除src
文件夾下的所有類型註釋,在dist
文件夾中保存編譯後的版本。編譯後的文件就是普通的能運行於瀏覽器的JavaScript文件。
//編譯前,/* @flow */記得寫。
/* @flow */
function fn1(x :number) {
return x;
}
fn1(1)
//編譯後
/* */
function fn1(x ) {
return x;
}
fn1(1)
結束語
本文討論了Flow各種各樣的類型檢查特性,展示了Flow如何幫助我們捕獲錯誤提高代碼質量。我們也看到瞭如何用可選的方式去逐個檢查JS文件,如何做類型推斷。
本人的博客
本人的github
本人的郵箱:[email protected]
本文章配合TypeScript食用更佳