flow的使用 | 掘金技術徵文

滴滴出行·DDFE發表於2016-12-30

作者:滴滴公共前端團隊 - J

前言:

很多人都開始一上來就應用到了 Vue.js 2.*,但並不是所有人都會發覺 Flow 的存在,其實熟悉 react 原始碼的應該不會陌生。所以呢,今天我們來普及一下這個東西到底對我們的工程成本有什麼收益呢?
它和 Babel 還有我們內部常用的 ESlint 有什麼外掛支援?
它和 Crockford 的 JSLint 或者後面的 JSHint 有什麼區別?

正文:

facebook 推出的 js 靜態型別檢查工具。

flow可以在程式碼執行前對型別錯誤進行檢查,包括:

  • 型別錯誤
  • 對null的引用
  • 以及可怕的 “undefined is not a function”
    flow 允許我們給變數新增型別

flow 的安裝和使用

安裝:
由於 flow 是用 OCaml 語言寫的,npm 上只有對應的二進位制包。

npm install --save-dev flow-bin複製程式碼

1.基本使用

安裝完成後我們在要執行靜態檢查的檔案跟目錄下執行一下 flow init ,之後會發現多處一個.flowconfig檔案,這個檔案告訴 Flow 在這個目錄下開始檢測。此外 .flowconfig 檔案可以進行一下更為高階的配置,比如僅包含一些目錄、忽略一下目錄等等(更深入的瞭解,請戳官網)。

對於需要使用 flow 進行型別檢查的 js 檔案,在開頭加入 @flow 的註釋

/* @flow */ 只要帶有這個註釋,都會進行型別檢測

或者

/* @flow weak */ 只對有加型別註解的變數進行型別檢測複製程式碼

例如:

/* @flow */
function multiple10 (num) {
  return num * 10
}

multiple10('20')

function getLength (str) {
  return str.length
}
getLength('3')
getLength([1,2,3])複製程式碼

接下來執行 flow check 看一下結果

3: return num * 10
          ^^^ string. The operand of an arithmetic operation must be a number.

Found 1 error複製程式碼

multiple10 函式中的型別轉換被 flow 標記出。

2.型別註解

注意上面例子中 flow 的報錯,只有 multiple10 中靜態型別錯誤被檢測中。對於 getLength 函式中引數 str 的型別是什麼呢? 從函式本身來分析,只要包含 length 屬性就都是合法的。 對於這種情況則可以為其新增“型別註解”,來明確的告訴 flow 這個值的型別。

function getLength (str: string) {
  return str.length
}
getLength('3')
getLength([1,2,3])複製程式碼

再來執行一下 flow check , 這時結果會提示我們[1,2,3]這個引數型別不對。

 12: getLength([1,2,3])
               ^^^^^^^ array literal. This type is incompatible with the expected param type of
  8: function getLength (str: string) {
                              ^^^^^^ string複製程式碼

3.自定義型別

很多時候,除了 number 、 string 這些基礎型別外,我們還會有一些自定義的型別,比如:

var someData = {
    id: 1,
    text: '選項1'
}複製程式碼

這時候可以在一個單獨的檔案中將 someData 申明瞭一個自定義型別。方式如下:

/* /decls/data.js.flow */
declare type SomeData = {
  id: number;
  text: strin;
}複製程式碼

然後在 .flowconfig 檔案中引入該申明檔案

[libs]
decls/複製程式碼

flow server

在大型專案中,如果每修改完程式碼,就執行以下 flow check ,然後等待看結果,顯然會被逼瘋的。flow 為我們提供了一個 flow server ,支援在後臺執行,並且只監測有修改的檔案。方法很簡單,只有一個命令

$> flow # 開啟一個後臺服務,輸出首次檢測結果
$> flow # 第二次使用flow,連線正在執行的後臺服務,輸出檢測結果
$> flow stop # 關閉flow server複製程式碼

babel+flow

由於 flow 中型別註解的語法不屬於 javascript 規範中的內容。所以在最終的程式碼中,我們需要移除flow的內容。flow 提供了 flow-remove-types 和 babel 外掛兩種方式,推薦使用 babel 外掛來完成這項工作。

  • flow-remove-types
    這種方法比較簡單粗暴: 安裝 flow-remove-types,然後執行命令。

    $> npm install -g flow-remove-types
    $> flow-remove-types src/ --out-dir build/複製程式碼
  • babel外掛
    安裝 babel 外掛

    $> npm install babel-plugin-transform-flow-strip-types複製程式碼

    babel 的 plugin 中加入該外掛

    {
    "presets": ["es2015", "stage-2"],
    "plugins": ["transform-runtime", "transform-flow-strip-types"],
    "comments": false
    }複製程式碼

    注意:在 babel6 的 babel-preset-react 的外掛中已經內建了 transform-flow-strip-types(flowtype.org/docs/syntax…),如果使用了 babel-preset-react 那麼無需再引入transform-flow-strip-types

eslint

eslint-plugin-flowtype 外掛,可以讓我們在 eslint 程式碼檢查中加入 flow 的書寫規範化檢查。使用方式也很簡單:

  1. 安裝

    $> npm install eslint-plugin-flowtype複製程式碼
  2. 在 .eslintrc.js 中設定 parser 為 babel-eslint
  3. plugin 中加入 flowtype
  4. eslint-plugin-flowtype 外掛中預設提供了一份基於優秀實踐總結出的 flow type 書寫規範配置,在 .eslintrc 檔案的 extend 中加入 "plugin:flowtype/recommended" 即可直接使用。
{
  "extends": [
    "standard",
    "plugin:flowtype/recommended"
  ],
  "plugins": [
    "flowtype"
  ]
}複製程式碼

完成後我們來看看效果。下面的程式碼

function getLength (str:string) {
  return str.length
}
getLength('3')複製程式碼

eslint 檢查結果會丟擲一個錯誤:型別註解的冒號後面丟失了空格。


  ✘  https://google.com/#q=flowtype%2Fspace-after-type-colon  There must be a space after "str" parameter type annotation colon
  /Users/didi/xiaoju/src/static-h5-src/750/driver/feedback/src/main.js:2:21
  function getLength (str:string) {
                       ^
✘ 1 problem (1 error, 0 warnings)複製程式碼

因為推薦的規範中:型別註解冒號後需要一個空格

 "rules": {
   ...
    "flowtype/space-after-type-colon": [
      2,
      "always"
    ],
    ...
 }複製程式碼

flow type 規範配置如:函式返回型別是否必須、型別註解冒號前後的空格、自定義的type 的名稱的命名方式等等,官方給出了很詳細說明和例子。注:針對flow的的規範規則配置前新增“flowtype/”

配置名稱 作用
boolean-style 型別註解中布林值使用boolean還是bool
define-flow-type 將型別註解標記為已定義,no-undef的檢查中不會出現報錯
delimiter-dangle Object和Tuple型別定義中分隔符使用規範
generic-spacing 泛型物件的尖括號中型別前後的空格規範
space-before-generic-bracket 泛型物件的尖括號前的空格規範
no-dupe-keys object型別的定義中是否有重複的屬性值
no-primitive-constructor-types 禁止使用原生的型別
no-weak-types 是否可以使用弱型別any、Object、Function
object-type-delimiter Object型別定義中,屬性之前分割符為分號/逗號(注:該屬性已被廢棄,需要使用分號來分割)
require-parameter-type 函式的引數是否需要型別註解
require-return-type 函式返回值是否需要型別註解
require-valid-file-annotation 檔案開頭@flow的寫法
require-variable-type 什麼樣的變數是需要型別註解
semi 使用type自定義型別語句結尾是否需要分號結尾
sort-keys Object型別定義中屬性排列順序
space-after-type-colon 型別註解分號後的空格規範
space-before-type-colon 型別註解分號前的空格規範
type-id-match 使用type自定義型別的名稱規範
union-intersection-spacing union型別、intersection型別連線符號\ 、&之間的空格規範
use-flow-type 將通過declare定義的自定義型別標記為已經被使用過,no-unused-vars的檢查中不會出現報錯
名詞說明

文中一些中文詞直接從英文文件中翻譯過來,可能有不準確的地方,這裡給出原文,避免歧義。

  • 型別註解:原文為 type annotations ,標記變數的型別
  • 自定義型別:原文為 type aliases, 類似C語言中的typedef,可以為已有型別定義一個新的名稱,或將一個複雜型別進行封裝,如

    type a = Array<String>
    type Person = {
       name: string,
       age: number
    };複製程式碼
相關連結

flow 官網(關於 flow的各種用法官網給出了詳細的例子):flowtype.org/
flow 簡短教程:www.youtube.com/watch?v=xWM…

「掘金技術徵文」活動:gold.xitu.io/post/58522d…


歡迎關注DDFE
GITHUB:github.com/DDFE
微信公眾號:微信搜尋公眾號“DDFE”或掃描下面的二維碼

flow的使用 | 掘金技術徵文

相關文章