FlowType簡易入門指北

  • 2019 年 12 月 5 日
  • 筆記

FlowType簡易入門指北

寫了一段時間JavaScript了,作為一個弱類型語言,無視類型判斷在開發過程中帶來了很多的好處,int與float的轉換、string與int的拼接。都可以直接通過一元運算符得到結果。 但同樣的,程式碼量上去了以後,整個項目會變得非常複雜。 在開發過程中很難看出一段程式碼執行後的結果,或者一個函數參數/返回值的結構。 有很多bug是在運行時才發現的。

比如一些常見的坑: $input.value + 1 // 如果input的值為 `2` 我們得到的結果卻是 `21` 以及在多人合作開發時,我們可能會提出一些公共的函數供其他人調用,例如: function numberAddComma (num) { return num // 添加千分位 } 如果不寫注釋的話,用的人很難去知道,這個參數到底是一個number類型還是一個string類型。

於是,我們就有了大佬們創造的FlowType,一個靜態類型檢查工具。 Flow可以在程式碼運行前對類型進行檢查,包括:

  1. 類型錯誤
  2. 對null的引用
  3. 最坑爹的undefined is not a function

Flow的安裝

我們可以通過直接安裝flow的npm包來應用。

npm i -D flow-bin

然後在package.json中添加一行命令:

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

然後我們在項目根目錄創建一個配置文件.flowconfig。 flow-bin提供一個命令生成一個空文件:

npm run flow init

在寫完程式碼後通過執行npm run flow即可進行校驗。

P.S. Flow只會檢查帶有@flow注釋標記的文件。

Flow的使用

Flow具有兩種類型檢查方式:

  1. 根據變數在程式碼中的運用來推斷變數的類型
  2. 通過事先聲明好的類型來進行檢查

通過程式碼來推斷類型

// @flow  function maxNum (nums) {    return Math.max.apply(null, nums)  }    maxNum(true)    function joinStr (arr) {    return arr.join(' ')  }    joinStr(123)

以上程式碼執行npm run flow後,我們就會在終端看到如下報錯資訊

第一處表示apply預期第二個參數需要是支援迭代的,而我們傳入的一個boolean類型變數是不支援的。 第二處則是提示我們number類型是沒有實現join這個方法的。

// @flow    function product(num1, num2) {      return num1 * num2  }    product(1, 2)

以上程式碼的執行是不會出錯的,因為兩個number相加,這個是沒有問題的。

以上的所有檢測都是由Flow判斷程式碼得來的,對我們現有程式碼的改動是非常小的,基本上就是在文件頭部添加一個@flow標識就能完成Flow的配置。

固定類型

當然,完全依賴Flow去判斷類型,對於一些基礎的類型還是很好用的。 但如果遇到一些比較複雜的情況,還是建議直接在程式碼中添加類型的描述。

一個小例子:

就像上邊的函數,+這個運算符既可以用在string也可以用在number上,所以在Flow自行判斷來看,這個參數的類型就是number | string,而我們想要限制他為一個number 就只能自己添加類型的描述了。 然後我們將result的類型改為string觀察一下:

這時我們就能看到拋出的異常,提示result的類型與函數numberAdd的返回值類型不匹配。 同理,如果我們在調用函數時傳入一個string,Flow也會提示我們,類型不匹配,這極大的避免了因為類型轉換帶來的bug。

這個是最基本的靜態類型檢查效果,或者我們可以提前定義一些特殊的數據格式。

然後我們在.flowconfig文件中添加它的引用。(如果沒有這個文件請在根目錄創建,用來進行一些Flow相關的參數配置,官網文檔有詳細的解說)

然後我們在一個文件中進行應用,創建一個函數,用來輸出我們的Person對象的兩個屬性。

/* @flow */    function logPerson(person: Person): void {      console.log(`          firstName: ${person.firstName}          age:       ${person.age}      `)  }    let person: Person = { // 後邊的 : Person 是可選的,因為會應用到我們前邊所說的`推斷類型`      firstName: 'Bellic',      age: 18  }    logPerson(person)

如果我們將變數person的age改為string類型,我們會看到如下的錯誤提示:

當然,關於類型,並不是只限定的只有一種類型,你可以在後邊添加多個類型,例如:

// @flow  function getResult (num: string | number): number {    console.log(num)      return 1234  }    let result1 = getResult(1)  let result2 = getResult('2')  let result3 = getResult(true) // error

對空值的處理

// @flow    const fs = require('fs')    function readFile(filePath) {      let buffer = fs.readFileSync(filePath)  }    readFile()

如果我們寫了這樣的一個函數,用來獲取文件對應的數據的,在下邊調用時沒有傳入filePath參數,Flow會給出我們提示:

強制要求我們對空值進行處理。

function readFile(filePath) {      if (!filePath) return ''      let buffer = fs.readFileSync(filePath)  }

這樣Flow就不會報錯了,避免了程式碼上線後出現與undefined/null相關的可怕問題。

在IDE中的使用

每次寫完程式碼,都要去執行一下npm run flow,其實也挺煩人的,所以,我們可以直接將Flow應用到編輯器上。 關於Flow的應用,因為Atom編輯器支援的還不太好,所以搞了VSCode來測試。 我所使用的是ESLint版本的

首先我們需要安裝Flow對應的eslint插件。

npm install --save-dev eslint eslint-plugin-flowtype

安裝完插件後,還需要去VSCode中安裝對應的Flow以及ESLint插件。

這些都完成後可能還會發現一些錯誤提示,類似:':' can only be used in a .ts file 這個是VSCode默認的一些錯誤檢查,解決這個只需要在IDE的配置文件中添加如下設置即可"javascript.validate.enable": false(command + ,)

這是我們就可以在IDE中直接看到Flow的類型檢查了。

使用VSCode還有一個好處,當你寫了一個應用了Flow的函數後,在調用函數時,游標懸浮在函數上,可以很直觀的看到函數的簽名:

以及如果我們將前邊定義的Person結構拿過來,在簽名中也是會直接體現出來的

移除Flow內容

因為Flow的語法並不是標準的JavaScript語法,所以我們要在程式碼最終上線前移除Flow相關的程式碼(主要是那些固定類型的描述,如果只是添加了@flow,直接應用即可)

flow-remove-types

這個程式會將你所有標有@flow的內容進行移除。。然後將移除後的程式碼生成後指定的目錄下

npm i -g flow-remove-types  flow-remove-types src/ --out-dir dist/  # src 源文件地址  # dist 生成後的地址

babel+webpack

安裝一個webpack插件

npm i -D flow-babel-webpack-plugin

然後我們修改 .babelrc文件,添加如下配置:

{    "plugins": [        "transform-flow-comments"    ]  }

然後在webpack.config.js文件中添加:

module.exports = {    plugins: [        new FlowBabelWebpackPlugin()    ]  }

在babel編譯JavaScript的同時也就會將Flow內容進行移除了。

參考鏈接

http://flowtype.org

關於Flow,個人感覺是一個挺好玩的東西,而且最重要的是,這個檢查器的遷移成本非常低,低到甚至只是添加一個/* @flow */就可以使用Flow相關的功能了。 對現有程式碼的破壞幾乎可以忽略不計。 只做一件事,且命中痛點,避免因變數類型造成的程式bug

Tags: