為什么要在javascript中進行靜態(tài)類型檢查.Part1[譯]

本文為翻譯文章鳞尔,原文鏈接見文末

作為一個JavaScript開發(fā)者,你可以編寫一整天編寫也不會遇到任何靜態(tài)類型檢查得問題。那么為什么要自找麻煩得去學習它呢智玻?
然而學習靜態(tài)類型并不僅僅是一個思維拓展的訓練。如果你愿意花點時間來學習一些靜態(tài)類型的優(yōu)勢芙代、劣勢以及使用的案例吊奢,那將會極大的幫助你進行編碼。
怎么樣纹烹,有意思吧页滚?要是你感興趣的話,那接下來四個部分將會向你詳細解釋铺呵。

一裹驰、定義

理解靜態(tài)類型最快捷的方式就是它和動態(tài)類型進行對比。

A language with static types is referred to as a statically-typed language. On the other hand, a language with dynamic types is referred to as a dynamically-typed language.

靜態(tài)類型語言和動態(tài)類型語言得核心區(qū)別在于片挂,靜態(tài)類型語言(statically-typed languages)會在編譯時(compile time)進行類型檢查幻林,而動態(tài)語言(dynamically-typed)則是在運行時進行類型檢查(runtime)。

這里又多了一個概念:什么是“類型檢查”(type-checking)音念?

為了解釋這個概念沪饺,我們可以將Java于JavaScript的類型對比看一下。

這里的類型(Types)指的是一個數(shù)據(jù)被定義的類型闷愤。

舉個例子整葡,在Java中如果你定義一個boolean值:

boolean result = true;

這個變量就有了一個正確的類型,因為boolean類型的聲明和這個變量給定的值是相符的讥脐。

在另一方面遭居,如果你嘗試這么來聲明這個變量:

boolean result = 123;

由于變量result有一個錯誤的類型,因此會編譯失敗攘烛。這里我們明確地聲明了result是一個boolean類型魏滚,但是它卻用整型值123賦給了它。

JavaScript和其他一些動態(tài)類型語言有著不同的處理方法坟漱,他們允許上下文環(huán)境來確定數(shù)據(jù)需要被定義為什么類型:

var result = true;

長話短說鼠次,靜態(tài)類型語言需要你在能夠使用這個變量之前定義它的類型。而動態(tài)類型語言則不同。JavaScript中變量類型是被“隱去”的腥寇,而Java中則是顯式聲明的成翩。

類型檢查將會確保并且強制使你的變量類型(constant, boolean, number, variable, array, object)和你已經(jīng)定義和預期的內(nèi)容相符。例如:你已經(jīng)確定“這個方法總是會返回一個string型”赦役。當程序運行的時候麻敌,你可以很安全地假設(shè)它會返回一個string型。

當出現(xiàn)一個類型錯誤時掂摔,靜態(tài)類型檢查和動態(tài)類型檢查的差異就凸顯出來了术羔。在靜態(tài)類型語言中,類型檢查發(fā)生在編譯階段乙漓。在動態(tài)類型語言中级历,只有在程序運行了一次的時候錯誤才會被發(fā)現(xiàn),也就是在運行時叭披。

這就意味著寥殖,對于寫動態(tài)類型語言的程序員而言,即使代碼中包含了會在運行時阻止腳本正常運行的錯誤類型涩蜘,這段代碼也可以通過編譯嚼贡。

在另一方面,如果一個寫靜態(tài)語言的程序員寫了一段包含了類型錯誤的代碼同诫,那么除非修復這個錯誤粤策,否則會一直編譯失敗。

A new era of JavaScript

因為JavaScript是一種動態(tài)類型語言误窖,因此你可以定義各種變量掐场、方法、對象而不需要聲明它的類型贩猎。

var myString = "my string";

var myNumber = 777;

var myObject = {
  name: "Preethi",
  age: 26,
};

function add(x, y) {
  return x + y;
}

這非常方便,但有時確不是那么理想萍膛。這也就是為什么想FlowTypeScript這樣的工具最近開始走入人們的視野吭服,帶給JavaScript開發(fā)者使用靜態(tài)類型的選擇。

Flow時Facebook開發(fā)和發(fā)布的一個開源的靜態(tài)類型檢查庫蝗罗,它允許你逐漸地向你的JavaScript代碼中添加類型艇棕。

TypeScript是一個會編譯為JavaScript的超集(盡管它看起來幾乎像一種新的靜態(tài)類型語言),這意味著串塑,它使用起來會感覺和JavaScript很像沼琉,并不難上手。

不論是使用上面哪種工具桩匪,當你想要使用類型時打瘪,你會明確告訴工具哪個(些)文件需要類型檢查。對于TypeScript,你需要將.js文件拓展名改為.ts闺骚。對于Flow彩扔,你需要在文件的頂部引入一段標注@flow

一旦你聲明了想要對某一個文件進行類型檢查僻爽,你需要使用他們各自的語法去定義類型虫碉。這兩個工具的一個區(qū)別在于,F(xiàn)low是一個類型“檢查器”而不是一個編譯器胸梆。TypeScript則是一個編譯器敦捧。

我相信類似Flow和TypeScript這樣的工具為JavaScript帶來了一個跨世代的轉(zhuǎn)變與提高。

下面我會從這四個部分來談一談“在javascript中進行靜態(tài)類型檢查”:

注意我選擇在例子中使用Flow而不是TypeScript是因為我比較熟悉它。你可以根據(jù)你自己的目的與場景選擇一個對你來說合適的工具洋措。TypeScript同樣非常不錯济蝉!

話不多說,開搞菠发!

第一部分王滤、Flow語法快速人們

為了要理解靜態(tài)類型的優(yōu)勢和劣勢,你首先需要通過使用Flow來對靜態(tài)類型的語法有個基礎(chǔ)的認識滓鸠。如果你以前從來沒用過靜態(tài)類型雁乡,那么你可以需要花點時間來熟悉一下這種語法。

讓我們先來看看如何在JavaScript基本類型(以及像數(shù)組糜俗、對象踱稍、函數(shù)等)上應用。

boolean

下面這段代碼描述了JavaScript中的boolean

var isFetching: boolean = false;

注意悠抹,當你想要定義一個類型時珠月,你需要用下面這種語法:


image
image

number

這里指的是IEEE 754下的浮點型。不像許多其他的程序語言楔敌,JavaScript并不會定義不同的數(shù)值類型(例如interger啤挎、short、long和float points)卵凑。而是所有的數(shù)值類型都會被存儲為雙精度浮點數(shù)庆聘。因此,你只需要一種數(shù)據(jù)類型來定義所有的數(shù)值變量勺卢。

備注:數(shù)值型number包含了InfinityNaN

var luckyNumber: number = 10;
var notSoLuckyNumber: number = NaN;

string

下面是一個string類型

var myName: string = 'Preethi';

null

下面是一個null數(shù)據(jù)類型

var data: null = null;

void

在這里void描述的是JavaScript中undefined類型

var data: void = undefined;

注意要將nullundefined區(qū)別對待伙判。如果你像下面這么寫:

var data: void = null;

/*------------------------FLOW ERROR------------------------*/
20: var data: void = null                     
                     ^ null. This type is incompatible with
20: var data: void = null
              ^ undefined

由于undefinednull是不同的類型,而void類型應該屬于undefined類型黑忱,因此Flow會拋出一個錯誤宴抚。

Array

JavaScript中的數(shù)組類型勒魔。使用Array<T>這樣的語法來定義一個數(shù)組,其中數(shù)組的元素類型為T酱塔。

var messages: Array<string> = ['hello', 'world', '!'];

注意上面的代碼沥邻,用string替換了T,表示messages是一個字符串數(shù)組羊娃。

Object

JavaScript中的對象類型唐全。有幾種不同的方式來為對象添加類型限制。

你可以添加類型來描述對象的格式:

var aboutMe: { name: string, age: number } = {
  name: 'Preethi',
  age: 26,
};

你可以用對象來作為Map蕊玷,并給鍵和值都設(shè)置類型:

var namesAndCities: { [name: string]: string } = {
  Preethi: 'San Francisco',
  Vivian: 'Palo Alto',
};

你也可以僅僅定義一個對象為Object類型:

var someObject: Object = {};

someObject.name = {};
someObject.name.first = 'Preethi';
someObject.age = 26;

上面這段代碼使你可以不受限制得設(shè)置對象的鍵和值邮利,因此就類型檢查而言,這種方式并沒有增加太多的價值垃帅。

any

正如字面意思延届,它可以代表任何類型。any類型一定程度上避免了類型檢查贸诚,因此如非必要方庭,盡量扁面使用這個類型。

var iCanBeAnything:any = 'LALA' + 2; // 'LALA2'

有一個場景下比較適用:當你使用了一個擴展了系統(tǒng)原型的外部類庫(類似Object.prototype)酱固。
例如械念,你使用的類庫為Object.prototype擴展了一個叫doSomething的屬性。

Object.prototype.someProperty('something');

你的代碼很可能會報如下錯誤:

41:   Object.prototype.someProperty('something')
                       ^^^^^^ property `someProperty`. Property not found in
41:   Object.prototype.someProperty('something')
      ^^^^^^^^^^^^ Object

為了避免著各種情況运悲,你可以使用any類型:

(Object.prototype: any).someProperty('something'); // No errors!

Functions

給方法添加類型的最常見的用法是龄减,為該方法的參數(shù)和返回值添加類型檢查:

var calculateArea = (radius: number): number => {
  return 3.14 * radius * radius
};

你甚至可以給async方法和生成器(generator)添加類型:

async function amountExceedsPurchaseLimit(
  amount: number,
  getPurchaseLimit: () => Promise<number>
): Promise<boolean> {
  var limit = await getPurchaseLimit();

  return limit > amount;
}

這里可以關(guān)注一下,這段代碼是如何將第二個參數(shù)getPurchaseLimit聲明為一個返回Promise對象的函數(shù)的班眯。同時希停,
amountExceedsPurchaseLimit方法本身被聲明會返回一個Promise對象。

類型別名(Type alias)

類型別名(Type alias)是我最喜歡的一種用法署隘。它允許你使用已有的類型(number,宠能、string等)來組合成一個新的類型:

type PaymentMethod = {
  id: number,
  name: string,
  limit: number,
};

在上面這段代碼中,我創(chuàng)建了一個叫作PaymentMethod的新類型磁餐,包含了numberstring兩種類型棍潘。

可以這樣來使用PaymentMethod類型:

var myPaypal: PaymentMethod = {
  id: 123456,
  name: 'Preethi Paypal',
  limit: 10000,
};

通過給原始類型包裹一層新類型,你可以為它們創(chuàng)建一個新的類型別名崖媚。例如,創(chuàng)建NameEmail這兩個類型別名:

type Name = string;
type Email = string;

var myName: Name = 'Preethi';
var myEmail: Email = 'iam.preethi.k@gmail.com';

這么做的話恤浪,可以清楚的表明畅哑,NameEmail是指代不同的事物,而不僅僅是一個字符串水由。由于NameEmail是不可互換的荠呐,這么做可以幫助你避免混淆它們。

泛型(Generics)

泛型是一種對類型本身進行抽象的方法。什么意思呢泥张?看看下面的代碼:

type GenericObject<T> = { key: T };

var numberT: GenericObject<number> = { key: 123 };
var stringT: GenericObject<string> = { key: "Preethi" };
var arrayT: GenericObject<Array<number>> = { key: [1, 2, 3] }

我為類型T創(chuàng)建了一個抽象的概念呵恢,你可以使用任何類型來代替T。對于numberT來說媚创,Tnumber類型的渗钉;而對于arrayT來說,TArray<number>類型的钞钙。

如果你是第一次接觸這些類型鳄橘,確實可能會有些暈。不過相關(guān)的入門介紹馬上就要結(jié)束了芒炼。

Maybe

Maybe類型允許我們聲明一個包含nullundefined兩個潛在類型的值瘫怜。對于類型TTnullundefined三種類型本刽,意味著一個變量可能是T鲸湃、nullundefined三者之一。在類型定義前加上一個“?”就可以定義一個Maybe類型:

var message: ?string = null;

這段代碼表示message是string類型子寓、nullundefined暗挑。

你也可以用Maybe類型來表示一個對象屬性可能是某種類型T或者undefined

type Person = {
  firstName: string,
  middleInitial?: string,
  lastName: string,
};

通過將“?”放在屬性名middleInitial之后,你可以表明這個對象時可選的别瞭。

Disjoint unions(或操作)

這是創(chuàng)建你的數(shù)據(jù)模型的另一個強大的方法窿祥。當你的程序需要同時處理不同的數(shù)據(jù)類型,Disjoint unions會是一個很有用的方法蝙寨。換句話說晒衩,根據(jù)環(huán)境的不同,數(shù)據(jù)的結(jié)構(gòu)也會不同墙歪。

我們基于之前的泛型示例來拓展PaymentMethod類型听系。想象一種場景,在我們的一個應用中虹菲,包含了三類不同的支付方法靠胜。在這種情況下,你可以這么做:

type Paypal = { id: number, type: 'Paypal' };
type CreditCard = { id: number, type: 'CreditCard' };
type Bank = { id: number, type: 'Bank' };

你可以用disjoint union來定義PaymentMethod類型:

type PaymentMethod = Paypal | CreditCard | Bank;

現(xiàn)在支付方法將會是這三種類型中的一種毕源。關(guān)于disjoint union浪漠,在第二部分將會有更多的例子。

除了上面所述之外霎褐,F(xiàn)low還有一些其他特性有必要在這篇簡介中提一下:

1)類型推斷(Type inference):可能的話Flow會使用類型推斷的功能址愿。當類型檢查可以自動推斷出一個表達式的數(shù)據(jù)類型時,類型推斷就會介入進來冻璃。這個特性可以幫助避免過多的類型聲明响谓。

舉個例子损合,你可以這么寫:

/* @flow */

class Rectangle {
  width: number;
  height: number;

  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  circumference() {
    return (this.width * 2) + (this.height * 2)
  }

  area() {
    return this.width * this.height;
  }
}

即使這個類并沒有類型,F(xiàn)low依然可以進行一定的類型檢查:

var rectangle = new Rectangle(10, 4);

var area: string = rectangle.area();

// Flow errors
100: var area: string = rectangle.area();
                        ^^^^^^^^^^^^^^^^ number. This type is incompatible with
100: var area: string = rectangle.area();
               ^^^^^^ string

在這里我嘗試把area定義為string類型娘纷,但是在Rectangle類中嫁审,我們給widthheight定義的類型時number類型。因此赖晶,基于area方法的定義律适,它只能返回一個number類型。即使我們有顯式地給area方法定義類型嬉探,F(xiàn)low還是可以捕獲到這個錯誤擦耀。

需要注意的一點是,F(xiàn)low的維護人員建議涩堤,如果你需要導出(export)類定義眷蜓,為了在非本地上下文環(huán)境(context)下更容易找出錯誤原因,你最好還是要添加明確的類型定義胎围。

2)動態(tài)類型檢查(Dynamic type tests):這個特性意味著吁系,F(xiàn)low有能力確定一個變量在運行時的類型,因此當進行靜態(tài)類型檢查使Flow也可以使用這個能力白魂。當Flow拋出一個錯誤但是你需要讓Flow相信你的代碼是沒有問題的時候汽纤,這個特性就顯得很有用處。

在這里我不會更深入得講解更多細節(jié)福荸,因為這更多的是一個進階的特性蕴坪,針對這一點我希望能夠單開一篇來介紹。但如果你想要了解更多敬锐,可以看看這里背传。

語法介紹結(jié)束

這一部分我們了解了非常多的內(nèi)容。我希望這個總覽性質(zhì)的簡介可以對你有幫助台夺。如果你想要進一步探索径玖,我推薦你可以深入地讀一下這些文檔

結(jié)束了語法的學習颤介,我們可以進入下一個更有趣的部分 ==> 使用靜態(tài)類型的優(yōu)勢與劣勢

原文:Why use static types in JavaScript? (A 4-part primer on static typing with Flow)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梳星,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子滚朵,更是在濱河造成了極大的恐慌冤灾,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辕近,死亡現(xiàn)場離奇詭異韵吨,居然都是意外死亡,警方通過查閱死者的電腦和手機亏推,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門学赛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吞杭,你說我怎么就攤上這事盏浇。” “怎么了芽狗?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵绢掰,是天一觀的道長。 經(jīng)常有香客問我童擎,道長滴劲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任顾复,我火速辦了婚禮班挖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芯砸。我一直安慰自己萧芙,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布假丧。 她就那樣靜靜地躺著双揪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪包帚。 梳的紋絲不亂的頭發(fā)上渔期,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音渴邦,去河邊找鬼疯趟。 笑死,一個胖子當著我的面吹牛几莽,可吹牛的內(nèi)容都是我干的迅办。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼章蚣,長吁一口氣:“原來是場噩夢啊……” “哼站欺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纤垂,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矾策,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后峭沦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贾虽,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年吼鱼,在試婚紗的時候發(fā)現(xiàn)自己被綠了蓬豁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绰咽。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖地粪,靈堂內(nèi)的尸體忽然破棺而出取募,到底是詐尸還是另有隱情,我是刑警寧澤蟆技,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布玩敏,位于F島的核電站,受9級特大地震影響质礼,放射性物質(zhì)發(fā)生泄漏旺聚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一眶蕉、第九天 我趴在偏房一處隱蔽的房頂上張望砰粹。 院中可真熱鬧,春花似錦妻坝、人聲如沸伸眶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厘贼。三九已至,卻和暖如春圣拄,著一層夾襖步出監(jiān)牢的瞬間嘴秸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工庇谆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留岳掐,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓饭耳,卻偏偏與公主長得像串述,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子寞肖,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容