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