開發人員工具架構更新:將開發人員工具遷移至 TypeScript

Tim van der Lippe
Tim van der Lippe

本文是一系列網誌文章之一,旨在說明開發人員工具架構的變動及建構方式。

接續先前遷移至 JavaScript 模組遷移至網頁元件的系列網誌文章,我們今天仍是網誌文章系列,說明我們對開發人員工具架構的異動,以及這項工具的建構方式。(如果您還沒看過介紹影片,我們發布了將開發人員工具的架構升級為新式網頁的成果影片,並介紹改善網路專案的 14 大秘訣。)

在這篇文章中,我們會說明從 Closure Compiler 類型檢查工具改用 TypeScript 的 13 個月。

簡介

由於開發人員工具程式碼集較大,且您必須為負責編寫程式碼的工程師提供信心,因此有必要使用類型檢查工具。為此,我們在 2013 年採用 Closure Compiler。開發者讓開發人員工具工程師可以放心地進行變更;Closure 編譯器會執行類型檢查,確保所有系統整合的類型都正確無誤。

不過,隨著時間過去,替代類型檢查工具在現代網站開發中便變得相當受歡迎。兩個值得注意的範例為 TypeScriptFlow。 此外,TypeScript 也成為 Google 的官方程式設計語言。 雖然這種新型檢查工具的熱門程度越來越高,但我們也注意到,原本應由類型檢查工具攔截的運送迴歸問題。 因此,我們決定重新評估選擇的類型檢查工具,並規劃後續在 DevTools 上開發的步驟。

評估類型檢查工具

由於開發人員工具已經使用類型檢查工具,因此我們必須回答以下問題:

我們要繼續使用 Closure Compiler,還是遷移至新的型別檢查工具?

為了回答這個問題,我們對多種特徵進行類型檢查工具評估。由於我們使用類型檢查工具著重於工程師的信心,因此最重要的部分是類型正確性。換句話說,Google Cloud 翻譯工具在發現實際問題方面有多可靠?

我們的評估重點是針對已出貨的迴歸,並判斷出這些迴歸的根本原因。我們假設我們先前使用 Closure Compiler,因此 Closure 不會偵測到這些問題。因此,我們必須判斷任何其他型別檢查工具能否處理。

在 TypeScript 中輸入正確性

由於 TypeScript 是 Google 官方支援的程式設計語言,且這個使用率快速提升,因此我們決定先評估 TypeScript。 TypeScript 團隊本身也使用 DevTools 做為其中一個測試專案,追蹤自身與 JavaScript 類型檢查的相容性,因此是個值得注意的選擇。他們的基準參考測試輸出內容顯示 TypeScript 能夠擷取大量的類型問題,也就是 Closure 編譯器不見得能偵測的問題。這些問題中,有許多可能是我們運送迴歸的根本原因;因此,我們因此認為 TypeScript 是開發人員工具的可行選項。

在我們遷移至 JavaScript 模組的過程中,我們發現 Closure Compiler 可以發現比以往更多的問題。改用標準模組格式後,Closure 對程式碼集的理解能力有所提升,也因此提升了類型檢查工具的成效。不過,TypeScript 團隊使用的 DevTools 基準版本比 JavaScript 模組遷移作業還早。因此,我們必須釐清改用 JavaScript 模組的過程,是否也能減少 TypeScript 編譯器偵測到的錯誤數量。

評估 TypeScript

開發人員工具提供至今已有超過十年,並已發展為規模相當大且功能豐富的網頁應用程式。 撰寫本網誌文章時,開發人員工具包含約 150,000 行第一方 JavaScript 程式碼。我們在原始碼上執行 TypeScript 編譯器時,會造成大量錯誤的困擾。 我們已經瞭解,雖然 TypeScript 編譯器所發出的程式碼解析相關錯誤較少 (約 2,000 個錯誤),但程式碼集發生其他與類型相容性相關的錯誤,

由此可見,雖然 TypeScript 能夠理解如何解析類型,但在我們的程式碼集內發現很大的類型不相容。手動分析這些錯誤後,TypeScript 往往正確。 TypeScript 可偵測這些情況和關閉的原因並不是原因,原因是 Closure 編譯器會將類型傾向為 Any,而 TypeScript 則會根據指派作業執行類型推論,並推論更準確的類型。因此,TypeScript 確實更能理解物件的結構,並發現有問題的用法

值得注意的是,開發人員工具中的 Closure 編譯器的用法包括 @unrestricted 的頻繁使用。 使用 @unrestricted 為類別加上註解,即可有效關閉該特定類別的 Closure 編譯器嚴格屬性檢查,這表示開發人員可以擴充類別定義,完全沒有類型安全。在開發人員工具程式碼集中,@unrestricted 的使用頻率不高,但我們找不到相關歷來背景資訊,但先前卻導致大部分程式碼集,以較不安全的作業模式執行 Closure 編譯器。

從迴歸分析中發現的 TypeScript 錯誤類型也出現重疊情況,導致我們相信 TypeScript 能夠預防這些問題 (前提是型別本身正確)。

正在撥打 any 通電話

現階段,我們必須決定改善 Closure Compiler 的使用情況,或是改用 TypeScript。 (因為 Google 或 Chromium 都不支援 Flow,因此我們必須放棄這個選項)。 根據 Google 工程師參與 JavaScript/TypeScript 工具的討論和建議,我們選擇使用 TypeScript 編譯器。 (我們最近也發布了一篇關於將 Puppeteer 遷移至 TypeScript 的網誌文章)。

TypeScript 編譯器的主要原因在於類型正確性提升,其他優勢還包括由 Google 內部的 TypeScript 團隊支援,以及 TypeScript 語言的功能,例如 interfaces (而非 JSDoc 中的 typedefs)。

選擇 TypeScript 編譯器時,我們必須大幅投入開發人員工具的程式碼集及其內部架構。因此,我們預估至少需要一年才能遷移至 TypeScript (2020 年第 3 季的目標)。

執行遷移作業

尚未解決的最大問題:我們要如何遷移至 TypeScript? 因為我們有 150,000 行程式碼,無法一次遷移。 此外,在程式碼集執行 TypeScript 時,也會發現多達數千行錯誤。

我們評估了多個選項:

  1. 取得所有 TypeScript 錯誤,並與「黃金」輸出內容進行比較。這個方法與 TypeScript 團隊的類似。合併衝突的極大缺點,就是合併衝突的出現率高,因為有數十位工程師在同一程式碼集中工作。
  2. 將所有有問題的類型設為 any這基本上會導致 TypeScript 隱藏錯誤。我們並未選擇此選項,因為遷移目標的目的在於確認類型正確性,因此遭到忽略。
  3. 手動修正所有 TypeScript 錯誤。這可能涉及修正數千個錯誤,而這很耗時。

儘管預期會有相當大的努力,但我們還是選擇使用選項 3。我們還會基於其他原因選用這個做法:例如,我們可稽核「所有」程式碼,並一次拒絕一次審查所有功能 (包括實作)。 從商業觀點來看,我們不會提供新價值,而是維持現狀。因此較難判斷選項 3 是正確的選項。

不過,我們強烈建議採用 TypeScript 來避免未來問題,特別是有關迴歸的問題。因此,這項引數小於「我們要增加新的業務價值」,更像是「確保不會失去現有的業務價值」。

TypeScript 編譯器的 JavaScript 支援

確認買方同意並制定在同一個 JavaScript 程式碼同時執行 Closure 和 TypeScript 編譯器的計畫後,我們一開始就先使用一些小型檔案。我們的做法大多由下而上:從核心程式碼開始向上移動,直到抵達高階面板。

我們能預先將 @ts-nocheck 新增至開發人員工具中的每個檔案,藉此平行處理工作。「修正 TypeScript」的程序是移除 @ts-nocheck 註解,並解決 TypeScript 找到的所有錯誤。這表示我們確信每個檔案都經過檢查,也盡可能解決許多類型問題。

一般來說,這個方法會遇到少數問題。 我們在 TypeScript 編譯器中遇到了幾個錯誤,但大多數的錯誤都是模糊的:

  1. 系統會將傳回 any 的函式類型的選用參數視為必要參數:#38551
  2. 將屬性指派給類別靜態方法的屬性會破壞宣告:#38553
  3. 透過 no-args 建構函式和具有 args 建構函式的父類別宣告子類別,會省略子項建構函式:#41397

這些錯誤強調,在 99% 的案例中,TypeScript 編譯器是可靠的建構基礎。 是,這些難以辨識的錯誤有時會造成開發人員工具的問題,但大部分時間都不夠模糊,我們可以輕鬆解決。

唯一造成誤解的問題是 .tsbuildinfo 檔案的非確定性輸出檔案:#37156。 Chromium 會要求同一個 Chromium 修訂版本的任一個版本提供完全相同的輸出內容。 很抱歉,Chromium 建構工程師發現 .tsbuildinfo 輸出內容不具確定性:crbug.com/1054494。為解決這個問題,我們必須 monkey-patch,.tsbuildinfo 檔案 (基本上包含 JSON) 並進行後續處理,以傳回確定性輸出:https://crrev.com/c/2091448 幸好,我們很快就移除了 TypeScript 團隊。感謝 TypeScript 團隊接納錯誤報告,並立即修正這些問題!

整體而言,我們非常滿意 TypeScript 編譯器的 (類型) 正確性。我們希望開發人員工具做為大型的開放原始碼 JavaScript 專案,有助於鞏固在 TypeScript 中對 JavaScript 的支援。

分析事後情況

在解決這類錯誤方面,我們已取得良好的進展,並慢慢增加 TypeScript 檢查的程式碼數量。 不過,我們在 2020 年 8 月 (完成遷移 9 個月) 後向我們確認,發現並沒有達到目前進度的期限。 我們其中一位工程師製作了分析圖表來呈現「TypeScriptification」(類型) 的進度 (我們為這項遷移作業取的名稱)。

TypeScript 遷移進度

TypeScript 遷移進度 - 仍有需要遷移的程式碼行追蹤

預估剩餘幾行數的估計值是從 2021 年 7 月到 2021 年 12 月,也就是幾乎超過期限的一年。與管理團隊和其他工程師討論後,我們同意增加正在遷移至 TypeScript 編譯器支援的工程師人數。我們希望這次的遷移作業能可以平行處理,使多位工程師同時處理多個不同檔案,不會彼此衝突。

目前,TypeScriptification 程序已成為整個團隊的工作。有了額外的協助,我們就能在 2020 年 11 月底、開始後的 13 個月,以及距我們預計初始預估時間前一年內完成遷移作業。

總共有 18 位工程師提交了 771 項變更清單 (類似於提取要求)。我們的追蹤錯誤 (https://crbug.com/1011811) 有超過 1200 則留言 (幾乎所有自動貼文來自變更清單)。我們的追蹤工作表針對要輸入類型的檔案、指派對象和變更清單,顯示了超過 500 列資料,

減輕 TypeScript 編譯器效能的影響

我們目前處理的最大問題是 TypeScript 編譯器的效能緩慢。考量到建構 Chromium 和開發人員工具的工程師人數,這個瓶頸是昂貴的。很遺憾,在遷移前無法找出這項風險,而且只在將大多數檔案遷移至 TypeScript 時發現,我們發現使用 Chromium 版本耗費的時間明顯增加:https://crbug.com/1139220

我們已向 Microsoft TypeScript 編譯器團隊通報這個問題,但他們很誤以為這是刻意行為。 我們希望他們會重新考慮這個問題,但在此期間,我們會盡力降低對 Chromium 效能的緩慢影響。

很遺憾,目前我們提供的解決方案不一定適合非 Google 員工。由於對 Chromium 的開放原始碼貢獻相當重要 (尤其是 Microsoft Edge 團隊的貢獻),我們積極尋找適合所有貢獻者的替代方案。然而,目前我們尚未找出適合的替代解決方案。

開發人員工具中的 TypeScript 目前狀態

目前,我們已從程式碼集移除 Closure 編譯器類型檢查工具,並僅採用 TypeScript 編譯器。我們可以編寫以 TypeScript 撰寫的檔案,並運用 TypeScript 專屬功能 (例如介面、泛型等),協助我們每天處理日常工作。我們進一步相信 TypeScript 編譯器能夠擷取類型錯誤和迴歸問題,這也是我們期望剛開始進行這項遷移作業時會發生的情況。像許多人一樣,這項遷移作業都很慢、過程複雜而且難免令人難過,但我們相信這是值得一提的成果。

下載預覽管道

考慮使用 Chrome Canary 版開發人員版Beta 版做為預設開發瀏覽器。這些預覽管道可讓您存取開發人員工具的最新功能、測試最先進的網路平台 API,並在使用者使用之前就在網站上發現問題!

與 Chrome 開發人員工具團隊聯絡

使用下列選項,討論文章的新功能和異動,以及其他與開發人員工具相關的事項。

  • 請透過 crbug.com 提交建議或意見回饋。
  • 如要回報開發人員工具的問題,請在開發人員工具中依序點選「更多選項」圖示 更多   >「說明」 >「回報開發人員工具的問題」
  • @ChromeDevTools 張貼推文。
  • 歡迎對開發人員工具的 YouTube 影片或開發人員工具秘訣 (YouTube 影片) 提供意見。