使用 Flow 寫更好的 JavaScript 代碼

lichenxi12 7年前發布 | 16K 次閱讀 JavaScript開發 JavaScript

你是不是常常發現自己在跟蹤代碼中的一個 bug,最后發現的錯誤只是某些本應該可以避免的簡單問題呢? 可能你只是按照錯誤的順序傳遞了參數,或者也許是你在嘗試傳遞一個字符串而不是一個數字?JavaScript 的弱類型系統,以及試著將變量在不同類型間強制轉換的意圖可能就是一整類 bug 的來源,而這些 bug 并不會存在于靜態類型語言之中。

Flow 是一個用于 JavaScript 的靜態類型檢查器,最早由 非死book 在 2014 年的  Scale 大會 上推出。它的目標是不需要開發人員去頻繁的修改實際代碼,就能找到 JavaScript 代碼中的類型錯誤,從而大大節省這方面時間與精力的消耗。同時,它也向 JavaScript 增加了額外的語法,以提供給開發者更多的控制能力。

在本文中,我會向你介紹 Flow 以及它的主要特性。我們將介紹如何設置它,如何在代碼中添加類型注釋,以及如何在運行代碼時自動刪除這些注釋。

安裝

Flow 目前可運行于 Mac OS X, Linux (64 位) 以及 Windows (64 位) 系統上。安裝它最簡單的辦法就是通過 npm:

npm install --save-dev flow-bin

然后將它添加到工程的 package.json 文件, 就在 scripts 部分下面:

"scripts": {
  "flow": "flow"
}

完成了此項工作,我們就做好了繼續探索其功能的準備。

入門

一個被命名為 .flowconfig 的配置文件必須被放在工程文件的根目錄之下。通過運行如下命令,我們可以創建出一個空的配置文件:

npm run flow init

放好了這個配置文件,你就能在終端上執行如下命令,來在工程文件夾或者任意子目錄下的代碼上運行 ad-hoc 檢查了:

npm run flow check

不過,這并非使用 Flow 的最有效方式,因為這樣做會導致 Flow 每次都會重新檢查整個工程文件結構。所以我們可以使用 Flow 服務器。

Flow 服務器會對文件進行增量檢查,意思就是它只會檢查被修改了的部分。服務器可以通過在終端上執行 npm run flow 命令來啟動。

在你第一次運行該命令的時候,服務器將會啟動并顯示初始的測試結果。該操作運行很快并且是增量的工作流。每次在你想要知道測試結果的時候,就在終端上運行 flow。完成了編碼環節之后,可以使用 npm run flow stop 來終止服務器的運行。

Flow 的類型檢查是 opt-in 的。這就意味著你無須一次性完成對所有代碼的檢查。Flow 只會為你檢查那些你想要檢查的那些文件,怎么讓 Flow 知道你想要檢查哪些文件呢?只需要在這些 JavaScript 文件頂部添加 @flow 作為注釋就可以了:

/*@flow*/

這有利于將 Flow 集成到一個現有工程中,因為你可以選擇你想要檢查的文件,然后逐步解決所發現的問題。

類型推斷

通常,類型檢查有以下兩種方法:

  • 通過注解: 我們指定期望的類型并把它作為代碼的一部分,類型檢查器會基于這些期望來對代碼進行評估。

  • 通過代碼推斷: 工具聰明到可以通過查看代碼被使用的上下文來推斷出類型,并基于此對代碼進行檢查。

使用注解的話,我們就得編寫一些額外的代碼,它們只會在開發期間被用到,最終在瀏覽器加載的 JavaScript 構建版本中被去掉。這樣做會增加一些額外的工作,也就是通過加入額外的類型注解來讓代碼“聽話”。

而在第二個場景中,代碼隨時“待命”, 故而縮減了程序員的工作量。我們不需要對代碼進行修改,因為它會自動地推斷出表達式的數據類型。這就是 類型推斷 , 它是 Flow 最重要的特性之一。

為了描述該特性,我們來看看如下示例:

/*@flow*/

function foo(x) {
  return x.split(' ');
}

foo(34);

這段代碼會在你運行 npm run flow 命令的時候在終端上報錯, 因為函數 foo() 需要的是一個字符串作為參數,而我們傳入的卻是一個數字。

報錯提示大概如下:

index.js:4
  4:   return x.split(' ');
                ^^^^^ property `split`. Property not found in
  4:   return x.split(' ');
              ^ Number

它明確說明了錯誤的位置和發生的原因。當我們將參數從數字修改為字符串,報錯就會消失,如下:

/*@flow*/

function foo(x) {
  return x.split(' ');
};

foo('Hello World!');

上面的代碼就不會報錯。在這里我們能發現的就是 Flow 理解了 split() 方法只適用于字符串,所以適合的 x 就必須是一個字符串。

可為空類型

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', 42);

Flow 在上面的代碼中不會發現任何錯誤,因為 + (加) 操作可以被用在字符串和數字之上, 而我們并沒有將add()的參數指定為數字。

在這種情況下,我們可以使用類型注解來指定想要的行為。Type 注解前綴一個 : (冒號),可以被放在函數參數、返回類型以及變量聲明之上。

如果我們向上面的代碼添加了類型注解,那么它就將如下所示:

/*@flow*/

function foo(x : number, y : number) : number {
  return x + y;
}

foo('Hello', 42);

這段代碼會顯示一個錯誤,因為函數希望得到的是一個數字作為參數,而我們提供的卻是一個字符串。

顯示在終端上的錯誤信息如下所示:

index.js:7
  7: foo('Hello', 42);
         ^^^^^^^ string. This type is incompatible with the expected param type of
  3: function foo(x : number, y : number) : number{
                      ^^^^^^ number

如果我們傳入的是一個數字而非“Hello”,就不會報錯了。類型注解對在龐大且復雜的 JavaScript 文件中指定想要的行為也同樣有用。

基于前面的例子,讓我們來看看 Flow 支持的一些其它的類型注解。

函數

/*@flow*/

/*--------- Type annotating a function --------*/
function add(x : number, y : number) : number {
  return x + y;
}

add(3, 4);

上面的代碼展示了一個變量和一個函數的注解。add()函數的參數以及返回值都被期望是數字的。如果傳入了任何其它的數據類型, Flow 就會拋出一個錯誤。

數組

/*-------- Type annotating an array ----------*/
var foo : Array<number> = [1,2,3];

數組注解采用 Array<T> 的形式,其中的 T 表示數組中單個元素的數據類型。在上述代碼中,foo應該是一個元素為數字的數組。

如下是類和對象的一個示例模式。唯一需要牢記的一點是我們可以使用 | 符號在兩個類型間執行 OR 操作。變量 bar1 被加上了注解,期望的是 Bar 類的模式。

/*-------- Type annotating a Class ---------*/
class Bar{
  x:string;           // x should be string       
  y:string | number;  // y can be either a string or a number
  constructor(x,y){
    this.x=x;
    this.y=y;
  }
}

var bar1 : Bar = new Bar("hello",4);

 

來自:https://www.oschina.net/translate/writing-better-javascript-with-flow

 

 本文由用戶 lichenxi12 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!