摘要:眾所周知,在大公司中進(jìn)行大的改革很難。目前公司有超過名開發(fā)人員,其中有個(gè)以上是前端。從年起,已經(jīng)在一些小規(guī)模團(tuán)隊(duì)中探索使用。在年的前端調(diào)查中,靜態(tài)類型系統(tǒng)呼聲最高。在我們的主倉庫中,絕大多數(shù)的公共依賴都已經(jīng)由做到了類型聲明。
特別說明
這是一個(gè)由simviso團(tuán)隊(duì)進(jìn)行的關(guān)于Airbnb大規(guī)模應(yīng)用TypeScript分享的翻譯文檔,分享者是Airbnb的高級(jí)前端開發(fā)Brie Bunge
視頻翻譯文字版權(quán)歸simviso所有,未經(jīng)授權(quán),請(qǐng)勿轉(zhuǎn)載:
參與翻譯人員名單
大家好,我的名字是 Bree,我在Airbnb工作。眾所周知,在大公司中進(jìn)行大的改革很難。這需要去說服很多人,同時(shí)又需要涉及大量的代碼遷移。我想要與大家分享我們是如何將 TypeScript 應(yīng)用到 Airbnb 這個(gè)公司的日常開發(fā)中的。
我很感謝你們能在這里,我知道你們完全可以披著時(shí)髦的毛巾在海邊娛樂。
我設(shè)想這里的每個(gè)人都會(huì)有這樣一些問題,你要為你的公司進(jìn)行重大的變革,同時(shí)這可能會(huì)被作為一個(gè)案例進(jìn)行研究。你現(xiàn)在是否在公司內(nèi)部積極的推動(dòng)將項(xiàng)目開發(fā)遷移到 TypeScript,為此我將提供一些技術(shù)和工具上的幫助。如果你之前已經(jīng)聽過并希望對(duì) TypeScript 了解更多。
首先,我們會(huì)介紹 TypeScript 是什么?規(guī)模化又意味著什么?對(duì)于將TypeScript規(guī)?;倪^程又有什么建議?基于這些問題和疑問,我會(huì)給出相應(yīng)的解答。我們?cè)撏ㄟ^什么樣的遷移策略將 JavaScript 逐步遷移到 TypeScript?
請(qǐng)大家快速舉手示意一下以方便我知道大概有多少人之前使用過 TypeScript。
cool,有這么多人!
還有一部分人沒有舉手。那么,我會(huì)給出一個(gè)快速介紹,這樣每個(gè)人都在同一起跑線上了。
假如我們有這么一個(gè) greeter 方法。它接收一個(gè) name 參數(shù),然后返回 hello + name。那么,如果我們傳入的是JSConf,它會(huì)和和氣的說 Hello,JSConf!
將剛才的代碼用 TypeScript 來表達(dá)就是這個(gè)樣子,可以看到他們很像。唯一的區(qū)別是我們?cè)谒膮?shù)這里使用了類型注釋。所以如果我們?cè)谖覀兊?TypeScript 項(xiàng)目中使用這個(gè)函數(shù)同時(shí)我們傳入一個(gè)字符串,可以看到編譯一切正常。
但是在這種情況下,如果我們傳遞的參數(shù)類型不是字符串,而是一個(gè)字符串?dāng)?shù)組。那么,如圖所示TypeScript 就會(huì)給我們一個(gè)錯(cuò)誤。即這是一個(gè)字符串?dāng)?shù)組,函數(shù)接收的參數(shù)類型是 string,不支持該分配。我們不需要再通過點(diǎn)擊刷新頁面這一流程來從我們的控制臺(tái)中查看錯(cuò)誤并確定該錯(cuò)誤所發(fā)生的位置??梢钥吹剑谖覀冚斎牒?,立即就從編輯器中得到了這個(gè)錯(cuò)誤。
我們也可以表達(dá)其他對(duì)象類型。這個(gè)接口描述了一個(gè)包含名字和姓氏的 person 對(duì)象。同時(shí),你可以定義更復(fù)雜的構(gòu)造類型。
TypeScript通常帶有一個(gè)編譯器。當(dāng)出現(xiàn)問題時(shí)就可以立馬告訴你。它還有一個(gè)可以與編輯器掛鉤的語言服務(wù)器,可以幫我們進(jìn)行自動(dòng)編譯,提供重構(gòu)相關(guān)提示等等。在這個(gè)例子中,我們已經(jīng)在這個(gè)組件中導(dǎo)入了withStyles react。這樣就可以自動(dòng)導(dǎo)入它所需要的數(shù)百個(gè)CSS屬性,包括內(nèi)聯(lián)文檔。神奇吧!我沒必要來回瀏覽文檔頁面,我在編輯器中就可以得到這些所有。通過在我們的代碼中使用類型,我們可以做更多的事情。我們只是稍微接觸了下TypeScript的類型限制,但是可以通過這個(gè)來幫你捕獲這種類型的錯(cuò)誤,以及支持它的工具。
關(guān)于TypeScript的內(nèi)容就到此,那關(guān)于規(guī)模化應(yīng)用這一部分呢?恩?這有個(gè)什么問題?感謝TypeScript幫我找出了這個(gè)錯(cuò)誤。如果你一時(shí)疏忽,輸入了一個(gè)錯(cuò)誤的變量,那你將會(huì)得到這樣的一個(gè)錯(cuò)誤提示。那么,在 TypeScript 中真的會(huì)發(fā)生這樣的事。所以,讓我們來修復(fù)下。
規(guī)?;瘯?huì)改變我們的交流方式。我以前在小團(tuán)隊(duì)的時(shí)候,如果你想要使用TypeScript。是的,聽起來很酷。那我們就用。但當(dāng)團(tuán)隊(duì)規(guī)模達(dá)到數(shù)百位工程師,同時(shí)代碼量也越來越多,那么交流方式就要發(fā)生改變。
我們需要進(jìn)行一些改變,即當(dāng)我們想要在我們的主倉庫中使用TypeScript的時(shí)候(這里指Powers | airbnb.com的主倉庫)。同時(shí)讓TypeScript成為前端主要開發(fā)語言。這個(gè)改變影響的人越多,那么必須遷移的代碼也就越多。
那我們用數(shù)字來說明規(guī)模化意味著什么。
airbnb(我所在的公司)擁有大量的JS代碼。在我們的主倉庫中有兩百萬行以上的JS代碼,以及100多個(gè)內(nèi)部npm包。我們有幾個(gè)分離的倉庫,通過打包到內(nèi)部的NPM注冊(cè)中心,這樣就可以跨倉庫共享。這真的有很多代碼,我甚至能看到一些 Backbone 的代碼,可以想一下JavaScript走過了多少年,它在Airbnb這里也走過了十多年。
所以我們有大量的開發(fā)人員在維護(hù)這些代碼。目前公司有超過1300名開發(fā)人員,其中有200個(gè)以上是前端。這些前端工程師大多數(shù)都參與了主倉庫的貢獻(xiàn)。這些數(shù)字給我們展現(xiàn)了當(dāng)時(shí)我們提議要使用TypeScript時(shí)所面臨的大環(huán)境。
那么在這種規(guī)模下,我們當(dāng)時(shí)是一個(gè)什么樣子呢?
每個(gè)月,我們都會(huì)將所有的前端工程師聚在一起開個(gè)比較有意思的會(huì),一起討論新的前端技術(shù)和選型。為了可以做到深思遠(yuǎn)慮,我們起草了提案。它對(duì)某個(gè)新技術(shù)進(jìn)行了諸如優(yōu)點(diǎn)、權(quán)衡、替代方案、針對(duì)退出方案的思考以及長(zhǎng)期擁有者等方面概述。大家會(huì)權(quán)衡這些提議的利弊。我們會(huì)站在團(tuán)隊(duì)的角度去決定向前邁出這步是否有意義,這確保了我們可以作為一個(gè)集思廣益的團(tuán)隊(duì),對(duì)所做的事情做出深思熟慮的決定。這樣可以避免在沒有正當(dāng)技術(shù)理由的情況下就“上車”。
從2016年起,Airbnb 已經(jīng)在一些小規(guī)模團(tuán)隊(duì)中探索使用 TypeScript。在2017年的前端調(diào)查中,靜態(tài)類型系統(tǒng)呼聲最高?;谶@個(gè)信號(hào),Joe(第二排那個(gè)Joe)和我起草了一個(gè)關(guān)于TypeScript的提案,并將它交給前端工作組。這項(xiàng)提案詳細(xì)說明了為什么在Airbnb使用TypeScript是有意義的。
讓我來講講主要的原因。airbnb的使命是要讓每一個(gè)人都感受到世界處處都是家(airbnb是一家旅游住宿的公司)。用戶對(duì)我們的產(chǎn)品提出的每一個(gè)建議都能會(huì)讓我們向著這個(gè)目標(biāo)更進(jìn)一步。這對(duì)于你正在開發(fā)的產(chǎn)品也是如此。
TypeScript可以幫助我們阻止bug的發(fā)生。
TypeScript還為開發(fā)人員提供大量的生產(chǎn)力效益和工具,像我們之前看到,自動(dòng)編譯和重構(gòu)。使用TypeScript,工程師可以更安全快速的遷移代碼。
在Airbnb我們引入了GraphQL 和Apollo,它可以使我們通過自定義的GraphQL模板來生成TypeScript類型
這意味著我們可以得到端到端之間的類型安全。因?yàn)榍岸撕秃蠖怂褂玫臄?shù)據(jù)類型共享了同一個(gè)事實(shí)上的定義源。后端工程師能夠在不影響客戶端的情況下對(duì)API進(jìn)行修改,而前端工程師則可以確信哪些數(shù)據(jù)將從服務(wù)器返回。類型不匹配一直是我們的主要bug所在。所以,這種端到端的類型安全性是一個(gè)主要的賣點(diǎn)。
4. 如何解決問題
這聽起來很棒! 但對(duì)于我們的初步提案,還有很多問題和疑慮。讓我們來對(duì)TypeScript進(jìn)行更深入的了解。
我們的主倉庫依賴了一些我們內(nèi)部的NPM包。為了獲得自動(dòng)完成和類型檢查的能力,我們需要先將它們轉(zhuǎn)換成TypeScript,這樣做是否值得?
這也是我們目前面臨的困境,我們的TypeScript 項(xiàng)目依賴于一個(gè)JS NPM包。那我們?cè)撊绾潍@取這個(gè)包的類型?
這看起來像是需要首先將這個(gè)包轉(zhuǎn)換為TypeScript。但這里有個(gè)問題,因?yàn)榫S護(hù)人員不允許我們對(duì)它做TS轉(zhuǎn)換操作,可能他們也不情愿這么做。因?yàn)樵谖覀兲岚傅脑缙?,我們并不確定是否要一直按照這個(gè)提案走下去。但是從另一個(gè)層面來講,我們使用TypeScript是為了可以讓開發(fā)人員可以有更好的體驗(yàn)。我們需要TypeScript提供的類型安全性。那么我們?cè)撊绾谓鉀Q看似雞和蛋的問題呢?
TypeScript有一個(gè)叫做聲明文件的功能。即一個(gè)以.d.ts為擴(kuò)展名的文件,通過它我們可以為JavaScript文件定義類型。
讓我們來看一個(gè)例子。
如圖所示,一起來看我們之前看到的greeter方法。它上面是對(duì)應(yīng)的.d.ts文件,方法里沒有實(shí)現(xiàn)細(xì)節(jié),它只描述了類型。TypeScript將它們組合到一起,這樣,在編譯是使用這個(gè)聲明文件,在運(yùn)行時(shí)使用這個(gè)原生的JS文件。
那么我們回到我們剛才提的問題(要不要一開始就轉(zhuǎn)換),看看聲明文件是如何提供幫助的。
當(dāng)然,如果那個(gè)項(xiàng)目已轉(zhuǎn)換為 TypeScript。我們就沒有必要再生成一個(gè).d.ts文件來作為TypeScript構(gòu)建時(shí)的一部分(因?yàn)樵谑褂肨S編程的時(shí)候,要通過它對(duì)原生JS進(jìn)行調(diào)用)。但我們認(rèn)為這不止一種選擇。相反,我們可以將聲明文件放在我們的 TypeScript 項(xiàng)目中。
另一個(gè)選擇則是我們可以創(chuàng)建一個(gè)多帶帶的NPM包,并將聲明所有聲明文件放入其中。這很棒,因?yàn)楝F(xiàn)在可以現(xiàn)在跨多個(gè)倉庫共享聲明這些文件。通過這些你可以在使用類似React時(shí),進(jìn)行相應(yīng)的類型檢查。在安裝React的同時(shí)你可以安裝@types/react包。
在這個(gè)@types/react包中針對(duì)React的5000個(gè)常用包做了類型聲明。@types/react與其他5000個(gè)其他包都在DefinitelyTyped 這個(gè)倉庫中,它由社區(qū)在維護(hù)。在我們的主倉庫中,絕大多數(shù)的公共依賴都已經(jīng)由DefinitelyTyped 做到了類型聲明。有活躍的社區(qū)氛圍是TypeScript 的一個(gè)主要賣點(diǎn)。我們也回饋了一點(diǎn)力量,相信在這個(gè)房間里也有人做出了貢獻(xiàn)。謝謝。
這些公共的NPM包的類型聲明已經(jīng)有DefinitelyTyped 在做了,但那些內(nèi)部的包該怎么辦?我們自己安裝了一個(gè)DefinitelyTyped鏡像,在它通過創(chuàng)建一個(gè)多帶帶的NPM域(@airbnb-types/*)。這樣,你只需要安裝@airbnb-types即可。這個(gè)倉庫的設(shè)置與DefinitelyTyped類似,所以我們可以在里面添加并發(fā)布這些內(nèi)部類型。
我們開源了一個(gè)starter 工具包,如果你有興趣的話,可以來參與下。它里面沒有類型定義,它只是在教你如何進(jìn)行一些配置以便于進(jìn)行測(cè)試或者發(fā)布自己的類型定義。
那么 TypeScript 究竟能幫忙避免多少 bugs 呢?近期,一個(gè)叫做“該不該做類型定義”的研究表明,在選擇了TypeScript 的 GitHub 倉庫中,有 15% 的 bugs 得到了避免。
在我們內(nèi)部,有一個(gè)記錄生產(chǎn)環(huán)境事故的流程。這個(gè)流程的本意并不是為了責(zé)怪誰,而是要從錯(cuò)誤中進(jìn)行學(xué)習(xí),這樣我們之后就不會(huì)再犯類似的錯(cuò)誤。所以我坐下來讀了六個(gè)月的總結(jié)報(bào)告,閱讀這些總結(jié)報(bào)告很有意思。我最喜歡的就是未捕獲的異常以及危險(xiǎn)的參數(shù)計(jì)算。
好吧,也許這些錯(cuò)誤的名字并沒有那么令人激動(dòng)。無論如何,我將這些錯(cuò)誤歸類為與 JavaScript 相關(guān)或無關(guān),以此確定哪些錯(cuò)誤可以通過使用TypeScript 來避免。
讓我們一起看個(gè)例子,使用TypeScript會(huì)帶來哪些幫助。我們對(duì)所分享的這個(gè) Input 組件進(jìn)行修改,通過一些設(shè)置來還原bug。用戶無法提交表單是因?yàn)樗辉倌芡ㄟ^驗(yàn)證,這是所分享的 Input 組件更改前的簡(jiǎn)化版本。
它接收一個(gè)叫onBlur的變量,并將其直接傳遞給input元素。所做的改變就是添加一個(gè)新的onBlur 事件處理。但這里有一個(gè)不明顯的bug,你能發(fā)現(xiàn)它嗎?
就是事件參數(shù)不再傳遞給onBlur prop。這就導(dǎo)致在好幾個(gè)不同倉庫中都出現(xiàn)了這同一個(gè)問題。
這里 Input 組件作為Redux Form的一部分進(jìn)行使用,期望得到一個(gè)事件或值,以便驗(yàn)證正常工作。如果沒有該事件,表單將不再通過驗(yàn)證,這就意味著提交按鈕始終處于禁用狀態(tài)。
TypeScript在這里是如何幫到我們的?從文檔中我們可以看到Redux Form有類型捕獲約束。
onBlur 事件屬性必須傳遞一個(gè)事件或值。因此,如果我們使用了TypeScript下的Redux Form,那么在函數(shù)調(diào)用那里就可以看到一個(gè)當(dāng)沒有傳遞事件參數(shù)時(shí)所產(chǎn)生的錯(cuò)誤。
另一類常見的問題就是涉及嚴(yán)格的空值檢查。即對(duì)使用屬性來構(gòu)造或嘗試調(diào)用可能為null或undefined的內(nèi)容進(jìn)行檢查。你可能以前有見過這個(gè)錯(cuò)誤。
另一種是類型不匹配。當(dāng)我們嘗試使用彼此不匹配的類型時(shí),TypeScript就會(huì)提示我們。
所以現(xiàn)在我們對(duì)常見的檢查出來的問題有了更好的理解,TypeScript可以幫助預(yù)防這種bug。
那總體百分率是多少?(那個(gè)事故日志所表現(xiàn)的)38%!
我們發(fā)現(xiàn)有38%的事故導(dǎo)致了生產(chǎn)階段的bug。這些對(duì)我們用戶產(chǎn)生實(shí)際影響的bug,可以使用TypeScript來阻止。這對(duì)我們來說是個(gè)巨大的發(fā)現(xiàn)。它有助于將這種(積極)效果轉(zhuǎn)換到現(xiàn)實(shí)中。我們復(fù)制了一些BUG事件,并向大家展示了TypeScript所給出的Error提示,然后對(duì)bug進(jìn)行修復(fù)(也就是我們看到的bug提示燈泡滅掉了)。的確,我們也可以通過寫測(cè)試代碼來捕捉這些,但是通過靜態(tài)類型檢查可以額外增加一層保護(hù)層。因此,如果你所在公司有類似的歷史,那么你可能就有必要和懂TypeScript小伙伴一起來看一下這些問題在你們的代碼里所占的比例。
那么團(tuán)隊(duì)是否希望切換到TypeScript呢?我們?cè)趲讉€(gè)團(tuán)隊(duì)試用了TypeScript,專門針對(duì)之前沒有使用過TypeScript的團(tuán)隊(duì)來獲取更多的使用反饋經(jīng)驗(yàn)。我們幫他們?cè)O(shè)置好 TypeScript 環(huán)境,然后收集他們的反饋。在用了一段時(shí)間后,我們向他們發(fā)送了一份調(diào)查問卷,詢問他們是否應(yīng)繼續(xù)使用TypeScript。反饋結(jié)果是非??隙ǖ?。
我們建議使用這種試用期(的形式,其實(shí)就是金絲雀模式)來測(cè)試新技術(shù)或模式。前端工作組的開發(fā)也是基于這個(gè)形式來進(jìn)行的,因?yàn)樗仟?dú)立的,它可以很容易回滾到之前的狀態(tài)。這也對(duì)提案很有幫助,因?yàn)槲覀兛梢耘袛鄨F(tuán)隊(duì)是否真的喜歡使用TypeScript。
這里可能會(huì)有一些關(guān)于構(gòu)建時(shí)間上的擔(dān)憂。我們測(cè)量了,發(fā)現(xiàn)并沒有明顯的影響。我們?cè)谥鱾}庫啟用超過了500條eslint規(guī)則,也使用TypeScript eslint解析器。我們很高興地發(fā)現(xiàn)它們中的大多數(shù)都可以工作。如果我們?cè)趯硪獥売肨ypeScript的話,我們可以剝離類型,并最終得到大致相同的JavaScript。所以我們逐一記錄、思考、跟進(jìn),并且針對(duì)提出的問題和擔(dān)憂找到解決辦法。
與批評(píng)者合作并聽取他們的擔(dān)憂對(duì)我們來說非常重要,最后這些批評(píng)者中的大部分轉(zhuǎn)而會(huì)支持我們,我們的提案也從他們的反饋中變得更為健壯。
在充分解決了這些問題之后,針對(duì)所有前端工程師進(jìn)行了我們是否應(yīng)該采用TypeScript的調(diào)查。我們收到了肯定的回答之后,我們有足夠的證據(jù)向前推進(jìn),并通過這項(xiàng)提案。
在此基礎(chǔ)上,我們逐步擴(kuò)大了采用范圍。此時(shí),我們已經(jīng)度過了試驗(yàn)階段,這對(duì)于驗(yàn)證 TypeScript 和打好基礎(chǔ)是很有用的。我們已經(jīng)解決了早期的矛盾,并改進(jìn)了工具和文檔,所以之后團(tuán)隊(duì)成員會(huì)更容易入門。
我們一直有與 TypeScript 團(tuán)隊(duì)進(jìn)行著聯(lián)系。并幫忙解決一些問題,比如,更好的默認(rèn)屬性優(yōu)先級(jí)處理。在這個(gè)階段,我們自己內(nèi)部的 TypeScript 社區(qū)也得到了發(fā)展。但是大部分 Airbnb 的員工還不知道 TypeScript。這也意味著更多的人可以去幫助和回答他們的問題。接下來我們將會(huì)進(jìn)入測(cè)試狀態(tài),團(tuán)隊(duì)可以選擇使用它。為了幫助團(tuán)隊(duì),我們創(chuàng)建了內(nèi)部文檔和風(fēng)格指南,并舉辦了一些學(xué)習(xí)課程。我們建立了一個(gè)聊天組,一個(gè)內(nèi)部的類Stack Overflow,谷歌Email主題,github組,來供組內(nèi)成員交流。我們想確保人們能得到他們需要的幫助。最后一步是將 TypeScript 完全普及化。此時(shí)就意味著它是穩(wěn)定狀態(tài),每個(gè)人都應(yīng)該開始使用它。
我們目前正在努力地去接近這個(gè)目標(biāo)。剩下的步驟就是鞏固風(fēng)格指南、文檔、加強(qiáng)內(nèi)部培訓(xùn)和遷移更多代碼。到目前為止,我們大約有50%的團(tuán)隊(duì)使用 TypeScript,在主倉庫中,有10%的文件已經(jīng)被轉(zhuǎn)換成 TypeScript。通過這種漸進(jìn)的方法,使團(tuán)隊(duì)遷移至 TypeScript 的過程更加順暢。
如果從第一天開始就要求每個(gè)人應(yīng)該使用TypeScript,那么一個(gè)接一個(gè)的人就會(huì)遇到同樣的問題。相反,我們先在小范圍內(nèi)使用 TypeScript ,然后總結(jié)一些經(jīng)驗(yàn)技巧。當(dāng)我們準(zhǔn)備把它大規(guī)模推廣時(shí),這些經(jīng)驗(yàn)技巧也會(huì)用得上。
6. 遷移策略
我們已經(jīng)探索出了幾種將代碼遷移至 TypeScript 的方式。我們最初的遷移策略的是混合使用 JavaScript 、TypeScript。
讓我們看看,在主倉庫中這個(gè)策略是如何進(jìn)行的。這是我在 airbnb.com 上找到的一個(gè)簡(jiǎn)化版本,并且給它們起了一個(gè)比較合理的名字。所以這里不存在公司的隱私信息。
讓我們放大homes 項(xiàng)目,看看使用混合策略轉(zhuǎn)換它會(huì)是什么樣子。
我們添加了一個(gè)TypeScript配置文件,并將各個(gè)文件從js重命名為ts或jsx重命名為tsx。如果TypeScript 報(bào)錯(cuò)了,那我們動(dòng)手去修復(fù)他們吧。TypeScript 的一個(gè)很棒的特性是,在編譯和運(yùn)行之前,并不需要轉(zhuǎn)換所有代碼。這個(gè)配置選項(xiàng)(allowJS)允許 javascript 和 TypeScript 文件共存。在這一點(diǎn)上,我們可以看到網(wǎng)站仍能繼續(xù)運(yùn)行。我們不需要暫停開發(fā)而去遷移整個(gè)項(xiàng)目,我們可以挨個(gè)遷移文件。我們會(huì)重復(fù)這個(gè)過程,直到整個(gè)項(xiàng)目被遷移。
在關(guān)于遷移的話題上,我想花些時(shí)間和大家分享一些我們認(rèn)為有用的技巧。第一個(gè)是$TSFixMe。
我們通過TypeScript的any類型添加了一個(gè)全局類型別名,這意味著它可以為任何類型。我們將它稱之為$TSFixedMe,表名我們?cè)诖a向TypeScript遷移完成后,再來將類型修正。平時(shí)最佳實(shí)踐是避免使用any,因?yàn)樗鼤?huì)造成類型安全丟失,但它在遷移過程中會(huì)很有幫助。
使用@ts-ignore注解可以做到忽略下一行錯(cuò)誤。正確地輸入一個(gè)文件可能涉及一些深層依賴鏈解析(類似于復(fù)雜對(duì)象)。我們可以嘗試通過首先轉(zhuǎn)換子文件來避免這種情況,但有時(shí)這是不可避免的。因此,$TSFixedMe和@ts-ignore注解能夠幫助拆分這些內(nèi)容,同時(shí)則會(huì)增加這些檢查工作。這些都是暫時(shí)的,我們計(jì)劃添加類型覆蓋工具,并在后面我們改進(jìn)類型時(shí)提供幫助。
在JSX中,我們?cè)赗eact組件上使用propTypes 進(jìn)行運(yùn)行時(shí)類型檢查。在將jsx轉(zhuǎn)換為tsx的時(shí)候,我們可以刪除proptypes直接用TypeScript,也可以在proptypes基礎(chǔ)上添加TypeScript。在我們所分享的react項(xiàng)目中,我們想保留傳參類型,以便別人使用的時(shí)候仍然可以獲得運(yùn)行時(shí)檢查。為了避免重復(fù)兩次類型聲明,那就需要與這些類型保持同步。我們創(chuàng)建了一個(gè)Props類型,通過它將給定的propTypes和defaultProps來派生出一個(gè)TypeScript類型。這樣,propTypes和defaultProps組合并得到這個(gè)最終類型。如果你好奇它是如何工作的,你可以查看我在gist上分享的代碼片段。
最近我們已經(jīng)在使用修訂遷移策略All-in TS進(jìn)行實(shí)驗(yàn)。讓我們回過來在看看這個(gè)Homes項(xiàng)目,然后對(duì)它們進(jìn)行使用all in策略,然后在看它工作怎么樣。
我們從js形式的文件開始,我們把所有的文件都改成TS形式的,然后讓項(xiàng)目編譯??赡芪覀兪褂靡恍┍任覀兿胍母鼘捤傻念愋停鋵?shí)我們已經(jīng)開啟了TS最嚴(yán)格的檢查配置。
然后我們接下來再繼續(xù)改進(jìn)類型,移除ts fix語句,比如@ts-ignore(@ts-ignore 注釋隱藏 .ts 文件中的錯(cuò)誤)。這與js和ts混合策略相比起來有一些優(yōu)勢(shì)。通過類型逐步改進(jìn)比通過文件逐步改進(jìn)更為簡(jiǎn)單(兩種策略的對(duì)比)。如果你正在開發(fā)一個(gè)新功能,你只需要關(guān)注新添加的類型,然后簡(jiǎn)單的修復(fù)這個(gè)它即可,而不是先轉(zhuǎn)換整個(gè)文件來修復(fù)所有錯(cuò)誤,然后再添加你所需要類型。
不用重命名文件也意味著更方便查看。有時(shí)候,如果一個(gè)文件在一次提交被重命名,然后在別的提交中修改。他們會(huì)在code review中多帶帶出現(xiàn),程序員必須要合在一起看才能知道變化了什么。后一種策略還能清楚地知道缺少哪些類型。
TypeScript類型推導(dǎo)能力十分強(qiáng)大,我們可以在編寫代碼的時(shí)候大量使用它。為了通過編譯,有些文件需要進(jìn)行一些TS Fixed。TS就可以推斷出剩余部分。
還有就是開發(fā)者們可能有一個(gè)固定的思維模式,他們并不會(huì)根據(jù)文件擴(kuò)展名來切換思維,于是就出現(xiàn)了比如為什么我不能在這里添加類型?為什么我不能在那里得到編譯錯(cuò)誤的疑問(.js和.ts混用)?那些類型在所有文件中都可以添加、使用、檢查。
這聽起來很不錯(cuò),但是我們?cè)撊绾芜w移我們整個(gè)代碼呢?對(duì)于大規(guī)模代碼修改而言,Codemod是一種十分強(qiáng)大的工具。拿最簡(jiǎn)單的形式來說,就好比是我們?cè)谖覀兊捻?xiàng)目中所使用的全局搜索和替換。你也許在你之前的IDE里面干過這件事(全局查找和替換),這些Codemod庫可以通過正則來替換,但它們很不穩(wěn)定,可能會(huì)因細(xì)微的代碼風(fēng)格變化而終止。
或者我們可以使用之前某人已經(jīng)講過的抽象語法樹。so,這就是這段代碼用AST(抽象語法樹)來表達(dá)的形式。如圖所示,左側(cè)的代碼都一 一對(duì)應(yīng)著右側(cè)抽象語法樹上的節(jié)點(diǎn)。所以為了好玩,我們想寫一個(gè)Codemod來反轉(zhuǎn)代碼中的所有標(biāo)識(shí)符。我們將我們的代碼作為輸入?yún)?shù),根據(jù)這個(gè)創(chuàng)建出AST(抽象語法樹),修改AST樹然后產(chǎn)生新的代碼。這里的關(guān)鍵是我們以編程方式進(jìn)行此更改。如果你手上需要修改的文件數(shù)并不多的話,我們可以一個(gè)個(gè)的去修改。但如果一旦文件數(shù)量達(dá)到數(shù)千個(gè)以上,這種手動(dòng)去修改的想法可能會(huì)令人感到十分心累。
因此我們Airbnb采用了Facebook的Jscodeshift來進(jìn)行這種大量的代碼重構(gòu)。這個(gè)轉(zhuǎn)換庫可以捕獲我們剛剛對(duì)該ast進(jìn)行的修改并且反轉(zhuǎn)標(biāo)識(shí)符。我們找到與標(biāo)識(shí)符對(duì)應(yīng)的所有節(jié)點(diǎn),用名字反轉(zhuǎn),用新節(jié)點(diǎn)去替換這些節(jié)點(diǎn),然后得到新的代碼。Missy Elliott(歌手)也將會(huì)我們感到自豪,所以我們反轉(zhuǎn)了它。
我們拿到了代碼并且重新改裝,找到了成員的標(biāo)識(shí)符然后翻轉(zhuǎn)它。yeah!
astexplorer.net這個(gè)網(wǎng)站無法幫你掌握好說唱技巧,但可以幫助你查看你的Codemods。在這個(gè)網(wǎng)頁下,它有一個(gè)可以通過源代碼輸出對(duì)應(yīng)的AST樹的功能,以及在你對(duì)代碼的改變同時(shí)反映到AST樹上。
我也在DefinitelyTyped 這個(gè)庫提交了關(guān)于Jscodeshift的PR,這樣的話可以來降低大家在使用TypeScript與Codemod的交互門檻。
在將JavaScript代碼遷移到TypeScript時(shí),有這幾種模式。對(duì)于react組件,我們一次次的將靜態(tài)類屬性移動(dòng)到class body里面。創(chuàng)建一個(gè)PropsType表示react生命周期方法。我們將它們編碼為Codemod,以便我們可以在更多代碼上重復(fù)運(yùn)行它們。我們通過使用一個(gè)叫作TS Migrate的工具來將它們進(jìn)行打包。這個(gè)工具的功能是當(dāng)如一個(gè)JS項(xiàng)目,然后得到一個(gè)編譯好的TS項(xiàng)目。隨著時(shí)間的推移,你仍然需要慢慢找到類型,但它為你提供了一個(gè)工作前提。我們將此工具應(yīng)用于我們的內(nèi)部分享的React組件庫,現(xiàn)在在我們的網(wǎng)站上已經(jīng)頻繁地使用。我們有內(nèi)部的類型定義庫(DefinitelyTyped),但是因?yàn)閞eact分享組件庫的快速發(fā)展,所以做到與時(shí)俱進(jìn)地更新太難了。所以,我們想直接從源碼類型出發(fā),這也是我們遷移TS的第一個(gè)目標(biāo)。我們已經(jīng)將超過3萬行以上的代碼都進(jìn)行了TypeScript化,你們可能認(rèn)為我們整個(gè)團(tuán)隊(duì)花了四周的時(shí)間才能完成這個(gè)。事實(shí)上,我們用了一套我們自己的Codemod工具,僅需數(shù)分鐘就完成了。
我們使用來自proptypes的類型信息,同時(shí)使用$TSFixMe,并基于此來繼續(xù)進(jìn)行優(yōu)化。但即便如此,我們也生成了有意義的TypeScript聲明文件,這樣我們可以在其他倉庫中進(jìn)行使用。在這個(gè)例子中,我們可以看到需要合并的代碼行數(shù)多的有點(diǎn)可怕。通過使用TypeScript編譯器以及在可視化回歸測(cè)試的幫助下,我們將在CI上運(yùn)行測(cè)試。通過這些測(cè)試我可以很自信的說,我的這些改變不會(huì)對(duì)原來的系統(tǒng)產(chǎn)生任何不利的影響。當(dāng)然我們還能確保我們的站點(diǎn)仍舊在正常工作,并不需要回滾代碼。不可思議!
我們現(xiàn)在已經(jīng)將TS Migrate運(yùn)用在其它的一些地方,同時(shí)也在不斷優(yōu)化和迭代它。我們計(jì)劃在以后會(huì)將它運(yùn)用于更多的代碼上(JS代碼)。我們打算之后將它開源,這樣你們也能將它運(yùn)用在你們的自己的代碼遷移上。
我想給你一些我們可以從TypeScript遷移中得出關(guān)鍵點(diǎn),并且是可以廣泛應(yīng)用的。在大型組織中實(shí)施變革可能是一項(xiàng)挑戰(zhàn),但強(qiáng)有力的事實(shí)依據(jù)和相關(guān)問題和擔(dān)憂的解決,可以使我們信服。采用逐步變化的方式有助于減少摩擦并證明其價(jià)值。一條明確的遷移路線能幫助團(tuán)隊(duì)更好的轉(zhuǎn)向新的模式,同時(shí)好的工具也能促進(jìn)這個(gè)過渡的過程。
我之所以開始這個(gè)工作,是因?yàn)橹坝袀€(gè)產(chǎn)品組對(duì)我的工具感到失望。當(dāng)我得知公司內(nèi)部其他人也有這種改變的想法的時(shí)候,我便與他們合作并將之進(jìn)行下去。與其怨天尤人接受現(xiàn)狀,只有通過行動(dòng)才能發(fā)生積極的改變。所以我鼓勵(lì)你去追求那些可以讓你對(duì)組織充滿激情的事情,讓你和你周圍的人的生活變得更好。
感謝大家的傾聽,同時(shí)感謝AirBnb為這個(gè)項(xiàng)目作出貢獻(xiàn)的每一個(gè)人,尤其是臺(tái)下的Joe和Mohsen。還有對(duì)其他一些優(yōu)秀的Airbnb工程師表示感謝。我手上也有些TypeScript主題的小便簽和一些鑰匙鏈,先到先得,只限前30人。
感謝大家的傾聽
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109986.html
摘要:它不僅從前端移動(dòng)到后端,我們也開始看到它用于機(jī)器學(xué)習(xí)和增強(qiáng)現(xiàn)實(shí),簡(jiǎn)稱。由于其高使用率,年的現(xiàn)狀調(diào)查將其稱為采用的安全技術(shù)。機(jī)器學(xué)習(xí)框架在年的開發(fā)者峰會(huì)上,宣布了他們的機(jī)器學(xué)習(xí)框架的實(shí)現(xiàn),稱為。更高級(jí)別的用于在之上構(gòu)建機(jī)器學(xué)習(xí)模型。 2019,開發(fā)者應(yīng)該學(xué)習(xí)的16個(gè)JavaScript框架 showImg(https://segmentfault.com/img/remote/14600...
摘要:開發(fā)教程步步為營(yíng),掌握基礎(chǔ)技能發(fā)布機(jī)器學(xué)習(xí)速成課程為了幫助更多的人了解與學(xué)習(xí)機(jī)器學(xué)習(xí)相關(guān)的知識(shí)技能,發(fā)布了人工智能學(xué)習(xí)網(wǎng)站。更多相關(guān)內(nèi)容參考數(shù)據(jù)科學(xué)與機(jī)器學(xué)習(xí)實(shí)戰(zhàn)手冊(cè)。 showImg(https://segmentfault.com/img/remote/1460000013586587); 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱...
摘要:我們的目標(biāo)是找出最有職業(yè)投資回報(bào)率的主題和技術(shù)。比特幣在幾年內(nèi)增長(zhǎng)了若干個(gè)量級(jí)。比特幣倍拐點(diǎn)在這個(gè)圖表中,每個(gè)箭頭始于倍點(diǎn),指向價(jià)格修正后的最低點(diǎn)。 showImg(https://segmentfault.com/img/remote/1460000017919159); 圖:Jon Glittenberg Happy New Year 2019 (CC BY 2.0) 又到了一年的...
摘要:組件成為前端最基本的物料,融合在組件中的方案日趨成熟。組件成為最基本的前端物料,讓組件化更徹底在的調(diào)研報(bào)告中,開發(fā)者有愿意繼續(xù),有愿意繼續(xù)。需要留意的是,有表示對(duì)感興趣,因此獲得的最感興趣獎(jiǎng)。 簡(jiǎn)介: JavaScript 應(yīng)用范圍廣泛,靜態(tài)類型語言 TypeScript 會(huì)繼續(xù)得到更多開發(fā)者的青睞。 組件成為前端最基本的物料,CSS 融合在組件中(CSS in JS)的方案日趨成熟...
摘要:自動(dòng)化接入和升級(jí)方案通過命令行工具提供一鍵接入升級(jí)能力,同時(shí)集成到團(tuán)隊(duì)腳手架中,大大降低了工程接入和維護(hù)的成本。原始代碼經(jīng)過解析器的解析,在管道中逐一經(jīng)過所有規(guī)則的檢查,最終檢測(cè)出所有不符合規(guī)范的代碼,并輸出為報(bào)告。 引言 代碼規(guī)范是軟件開發(fā)領(lǐng)域經(jīng)久不衰的話題,幾乎所有工程師在開發(fā)過程中都會(huì)遇到,并或多或少會(huì)思考過這一問題。隨著前端應(yīng)用的大型化和復(fù)雜化,越來越多的前端工程師和團(tuán)隊(duì)開始重...
摘要:不過,根據(jù)伯克利大學(xué)的這篇文章來看,擁有豐富的開源庫,是開發(fā)者在選擇一門開發(fā)語言時(shí),最重要的因素。擁有超過個(gè)可用的開源庫,是目前世界上最大的開源庫集合。月份,我們發(fā)布了。這和年的情況是相反的。在的調(diào)查中,超過的受訪者表示他們正在使用。 showImg(https://segmentfault.com/img/bVblvke?w=693&h=300); 原文標(biāo)題:This year in...
閱讀 2938·2023-04-26 02:22
閱讀 2292·2021-11-17 09:33
閱讀 3144·2021-09-22 16:06
閱讀 1078·2021-09-22 15:54
閱讀 3541·2019-08-29 13:44
閱讀 1921·2019-08-29 12:37
閱讀 1327·2019-08-26 14:04
閱讀 1919·2019-08-26 11:57