用Flow提高前端健壯性

看一段常見代碼:

//例1
function foo(x) {
  return x + 10
}
foo('Hello!')

//例2
function main(params){
   //fn1函數獲取了一個數據
   var object = fn1(params)
   //fn2根據獲數據,產生一個結果
   var result = fn2(object)

   return result
}
複製代碼

例2很明顯,這個過程很是的‘黑’,若是你想知道object包含什麼數據的話,能夠:javascript

  1. 打印一下 console.log(object)
  2. 查看fn1的註釋,而且保佑它的註釋是正確,全面的
  3. 或結合1,2,而後仔細查看fn1的源碼,但願它不是很複雜

被上述步驟折磨完以後,終於能真正的寫點代碼了,可是依舊得很是當心,由於這裏還有另外一個函數:fn2java

在修改代碼的時候,得保證result這個結果沒有被影響,那麼如何保證呢?react

很簡單,重複上面的步驟,搞清楚result包含的數據,在測試的時候確保其數據跟原先的相同。 ...bash

動態類型一時爽,代碼重構火葬場 知乎傳送門:爲何說「動態類型一時爽,代碼重構火葬場」babel

是時候完全優化這個煩人的問題了angular2

引入類型系統

其實問題的根源就是由於javascript太靈活了,在代碼運行期間幾乎能夠作任何的修改,框架

沒有東西能夠在代碼層面保證 某個變量,某個函數 跟預期的一致函數

因此要加入類型系統來確保代碼的可靠性,在後期維護的時候一樣可以傳達出有效的信息工具

Flow & TypeScript

Flow是個JavaScript的靜態類型檢查工具,由Facebook出品的開源碼項目,問世只有兩三年,是個至關年輕的項目。簡單來講,它是對比TypeScript語言的解決方式。學習

會有這類解決方案,原由是JavaScript是一種弱(動態)數據類型的語言,弱(動態)數據類型表明在代碼中,變量或常量會自動依照賦值變動數據類型,並且類型種類也不多,這是直譯式腳本語言的常見特性,但有多是優勢也是很大的缺點。優勢是容易學習與使用,缺點是像開發者常常會由於賦值或傳值的類型錯誤,形成不如預期的結果。有些時候在使用框架或函數庫時,若是沒有仔細看文件,亦或是文件寫得不清不楚,也容易形成誤用的狀況。

這個缺點在應用規模化時,會顯得更加嚴重。咱們在團隊開發協同時,通常都是經過統一的代碼規範,來下降這個問題的發生,但JS語言自己沒法有效阻止這些問題TypeScript這樣的強(靜態)類型的JavaScript超集語言就開始流行,用嚴格的角度,以JavaScript語言爲基底,來從新打造另外一套具備強(靜態)類型特性的語言,就如同Java或C#這些語言同樣,這也是爲何TypeScript稱本身是企業級的開發JavaScript解決方案。

TypeScript存在的問題

TypeScript天然有它的市場,但它有一些明顯的問題:

  • 首先是JavaScript開發者須要再進一步學習,內容很多
  • 有必定陡峭的學習曲線
  • 已經在使用的應用代碼,須要整個改用TypeScript代碼語法,才能發揮完整的功用。這對不少已經有內部代碼庫的大型應用開發團隊而言,將會是個重大的決定,由於若是不往全面重構的路走,將沒法發揮強(靜態)類型語言的最大效用eg:angular2

因此許多現行的開源碼函數庫或框架,並不會直接使用TypeScript做爲代碼的語言,另外一方面由於TypeScript並不是是普及到必定程度的語言。 固然TypeScript也是個活躍的開源碼項目,發展到如今也有一段時間,它的背後有微軟公司的支持,全新打造過的Angular2框架中(由Google主導),也採用了TypeScript做爲基礎的開發語言

Flow你的新選擇

如今,Flow提供了另外一個新的選項,它是一種強(靜態)類型的輔助檢查工具Flow的功能是讓現有的JavaScript語法能夠事先做類型的聲明(定義),在開發過程當中進行自動檢查,固然在最後編譯時,同樣能夠用babel工具來移除這些標記

相較於TypeScript是另外從新制定一套語言,最後再通過編譯爲JavaScript代碼來運行。Flow走的則是非強制與非侵入性的路線。

Flow的優勢

  • 且易學易用 它的學習曲線沒有TypeScript來得高,雖然內容也不少,但半天學個大概,就能夠漸進式地開始使用
  • Flow從頭至尾只是個檢查工具 不是新的程序語言或超集語言,因此它能夠與各類現有的JavaScript代碼兼容,若是你哪天不想用了,就去除掉標記就是回到原來的代碼,沒什麼負擔

so

選擇flow.js工具而不選擇TypeScript強類型語言的緣由顯而易見? flow.js對工程的侵入性很小,無需大量的額外工做就能使用起來

從一個小例子演示

這種類型不符的狀況在代碼中很是容易發生,例如上面的例1:

function foo(x) {
  return x + 10
}

foo('Hello!')
複製代碼

x這個傳參,咱們在函數聲明時但願它是個數字類型,但最後使用調用函數時則用了字符串類型。最後的結果會是什麼嗎? "Hello!10",這是由於加號(+)在JavaScript語言中,除了做爲數字的加運算外,也能夠看成字符串的鏈接運算。想固然這並非咱們想要的結果。

聰明如你應該會想要用類型來當傳參的識別名,容易一眼看出傳參要的是什麼類型,像下面這樣:

function foo(number) {
  return number + 10
}
複製代碼
  • 若是在複合類型的狀況,例如這個傳參的類型能夠是數字類型也能夠是布爾類型,你又要如何寫得清楚?
  • 若是是個複雜的對象類型時,結構又該如何先肯定好?
  • 另外還有函數的返回類型又該如何來寫?

利用Flow類型的定義方式,來解決這個小案例的問題,能夠改寫爲像下面的代碼:

// @flow

function foo(x: number): number {
  return x + 10
}

foo('hi')
複製代碼

當使用非數字類型的值做爲傳入值時,就會出現由Flow工具發出的警告消息,像下面這樣:

[flow] Cannot call foo with 'hi' bound to x because string 1 is incompatible with number 2. (a.getting-start.js:6:5)

若是是要容許多種類型也是很容易能夠加標記的,假使這個函數可使用布爾與數字類型,但返回能夠是數字或字符串,就像下面這樣修改過:

// @flow

function foo(x: number | boolean): number | string {
  if (typeof x === 'number') {
    return x + 10
  }
  return 'x is boolean'
}

foo(1)
foo(true)
foo(null)  // 這一行有類型錯誤消息
複製代碼

在多人協同開發某個有規模的JavaScript應用時,這種類型的輸出輸入問題就會很常碰見。若是利用Flow工具的檢查,能夠避免掉許多沒必要要的類型問題

真實案例

可能你會認爲Flow工具只能運用在小型代碼中,其實否則,Vue源碼中大量使用flowjs中類型檢測:

此處輸入圖片的描述

Flow使用

此處輸入圖片的描述

  1. flow init
  2. // @flow 或 /* @flow */
  3. IDE插件 或 flow check 在Visual Studio Code中由於它內建TypeScript與JavaScript的檢查功能,若是要使用Flow工具來做類型檢查,須要在用戶設置中,加上下面這行設置值以避免衝突:

"javascript.validate.enable": false

4 . babel插件在編譯時就會一併轉換Flow標記

{
  "plugins": [
    "transform-flow-strip-types"
  ]
}
複製代碼

Flow支持的數據類型

Flow支持原始數據類型,以下面的列表:

  • boolean
  • number
  • string
  • null
  • void

類型別名&常見語法

// @flow
export type Test = {
  titleOne?: string,
  titleTwo: ?string
}
var a: Test = {titleOne:"3",titleTwo:4}

var b:string = ""
//any
export type NavigationGestureDirection = 'horizontal' | 'vertical';

type T = Array<string>
var x: T = []
x["Hi"] = 2 //有Flow警告

type TT = Array<Test>
var xx:TT = []
xx = [{titleOne: '1',
  titleTwo: false}]

type MyObject = {
  foo: number,
  bar: boolean,
  baz: string,
};

let val:MyObject = {foo:2,bar:false,baz:'444'};
var val1: MyObject = {foo:2,bar:false,baz:null};
var val2: MyObject = {foo:2,bar:false};
function method(val: MyObject):MyObject { return {foo:2,bar:false,baz:'2'}} 
class Foo { constructor(val: MyObject) { /* ... */ } }
複製代碼

React中的應用

若是你在React class裏面使用了React.PropTypes規範,你能夠對JSX上的attributes作靜態類型檢查:

var Hello = React.createClass ({
  propTypes: {
    name: React.PropTypes.string.isRequired
  }
  ...
});
//<Hello/> //Flow就會發現 缺乏屬性的錯誤
//<Hello name={42}/>//屬性類型的錯誤
複製代碼
import * as React from 'react';

type Props = {
  foo: number,
  bar?: string,
};

function MyComponent(props: Props) {
  props.doesNotExist; // Error! You did not define a `doesNotExist` prop.

  return <div>{props.bar}</div>;
}

<MyComponent foo={42} />
複製代碼

更多關於支持React的細節 請移步 https://flow.org/en/docs/react/components/

Flow將來的發展??

相關文章
相關標籤/搜索