本文經作者授權,翻譯總結自 TypeScript Team 的成員 orta 的個人部落格 《Understanding TypeScript's Popularity》。
原作者: orta
原文連結:https://orta.io/notes/js/why-typescript
翻譯:ycaptain
TypeScript 是一種非常受歡迎的 JavaScript 語言擴充。它在現有的 JavaScript 文法之上加入了一層類型層,而這一層即使被删除,也絲毫不會影響運作時的原有表現。許多人認為 TypeScript "隻是一個編譯器",但更好的了解其實是把 TypeScript 看作兩個獨立的系統:編譯器(即處理文法的部分)和語言工具(即處理與編輯器內建的部分)。通過獨立看待這兩個系統,就可以得到能夠解釋我們之前所做決策的兩個重要視角。
在 npm 上,TypeScript 的下載下傳量每年都在翻倍。如今(部落格釋出于 2021 年 4 月 14 日),它的每周下載下傳量約為 2000 萬次。而在去年 4 月,這一數字約為 1000 萬次。它仍保持着高速增長的趨勢,沒有任何放緩的迹象。
1. 我們是怎麼令它變得如此受歡迎的?
從 2.0 版本開始,TypeScript 每兩月定期釋出一個 release。但是現在我們放緩了釋出的節奏,改為每三個月釋出一次。其中我們會花一個月編寫新 features 并釋出 beta 版本,剩下兩個月對 beta 版進行測試和 bug 修複,這使得後續的釋出更加穩定。
1.1 大事件時間線
在我看來,下面這些都是讓 TypeScript 不斷突破流行極限的大事件:
- 2014 - 重寫 TypeScript,TS v1.1 - TypeScript 上線後,我們重新 review 了 TypeScript 的代碼庫,想明白了它到底是什麼,是以我們抛棄了原有的代碼,并以函數式風格重新編寫(差別于原來具有可變特性的類) - 這套架構一直沿用至今,它能夠在極少發生變更的程序中穩定高效地長時間運作。有人曾提到 TypeScript 的前身是 C++ 編寫的,這點我不能确定其真實性。
- 2015 - Angular 采用 TypeScript,TS v1.5 - 當時 Google 正在考慮為 Angular 建構他們自己的語言,而不是選擇使用 TypeScript。為了讓 TypeScript 能夠被納入考慮,TypeScript 打破了它的其中一條基本規則:不要過早實作 TC39。就這樣,TypeScript 支援了 experimentalDecorators。即使這一特性六年後還沒有被添加到 JavaScript 中,對于所有參與開發的人來說,這個結果也是完全值得的他們承受這項技術債務的。
- 2015 - 在 TypeScript 中支援 JSX, TS v1.6 - 這時 React 也成長為了一個非常受歡迎的 UI 庫。而 React 使用 JSX: 一種可以在 JavaScript 中高效地編寫 HTML 的 JS 語言擴充。TypeScript 對 JSX 的支援使得其他人可以進行 TypeScript 對 React 的支援(由 @types/react 維護,而不是内置于 TypeScript 中)
- 2016 - 未定義類型,基于控制流進行靜态類型分析 - TS v2.0 - 在 1.4 版本釋出的功能 union types 的基礎上,TypeScript 支援了 undefined 和 null 類型。這使得類型系統可以真正對大部分 JavaScript 代碼模組化。與此同時,代碼控制流分析使得 if 語句及其它的使用者代碼都能影響到變量在不同位置的類型分析結果。
- 2016 - 擁抱 DefinitedTyped, TS v2.0 - DefinitelyTyped 是一個由志願者們編寫的業餘項目。當時有一些其它類似 DefinitelyTyped 的系統,TypeScript 團隊采用了 DefinitelyTyped 并将 @types/x 的概念融入他們的編譯器當中。在采用和維護 DefinitelyTyped 的過程中,TypeScript 團隊進行了認真的測試并改進了工作流,這幫助它成為了 GitHub 上最活躍的 Repo 之一。關于 DT 的故事值得一讀。
- 2016 - 支援 JavaScript, TS v2.3 - 盡管當時已經有一些語言工具鍊支援 JavaScript 項目,對 JSDoc 的支援使得 JavaScript 項目即使不使用 TypeScript 也能獲得它的一些好處。這不僅開啟了 JavaScript 向 TypeScript 遷移之路,還為已存在的 JavaScript 項目提供了工具支援。
- 2018 - 在 Babel 中增加 TypeScript 支援, Babel 7 - 這是 TypeScript 成為代碼庫歸宿的起點。在 Babel 中增加 TypeScript 支援給 TypeScript 增加了一些限制(譯者注:isolated modules),但這是值得的。從此 TypeScript 不再需要從 eslint 遷移到 tslint 的複雜的遷移過程,而是像勾選一個複選框一樣輕松。
- 2018 - Composite Projects, TS v3.0 - 有許多方式可以處理大規模源碼庫,TypeScript 的處理方式就是 Composite Projects。通過使用 .d.ts 檔案作為項目的邊界,你可以在一個單體代碼庫内維護很多 TypeScript 子項目。這節省了時間和記憶體,最重要的是這使得你可以将其擴充成非常大的代碼庫。
- 2019 - Optional Chaining, TS v3.7 - 雖然這個清單中還遺漏了一些更大的語言特性,但是與 TC39 結合的完美特性 Optional Chaining 使得人們對 TypeScript 支援非常興奮。作為 JS 生态系統和工具的優秀參與者,将可選鍊特性加入 JavaScript 的過程是一個 TypeScript 對于自身預期定位的完美例子。TypeScript 應該多做這類項目。
- 2020 - esbuild / swc / sucrase - 這些新的編譯器和 JavaScript 運作時從第一個版本就支援 TypeScript 文法,任何基于這些工具的項目都可以直接使用 TypeScript。TypeScript 文法在 .ts 檔案中直接啟用進一步使得它作為 JavaScript 的内置擴充合法化。
- 2020 - 重寫使用者文檔 - 重寫文檔是我的工作,是以不要完全相信這部分描述,但是這些年 TypeScript 的文檔一直很薄弱。我與很多編譯器團隊裡的老人一起重新編寫面向使用者的文檔,并為他們提供一個幫助了解 TypeScript 的線上編輯器。這是對于回答語言問題的第一步,優秀的文檔可以讓編譯器團隊将注意力集中在編譯器上。
我的看法:TypeScript 之是以流行,是因為它不斷通過工具(DT / -- isolatedModules / JSDoc)降低入門門檻,與其它工具群組織合作(Babel / TC39),并通過漸進式的方式展示工具和類型安全在 JavaScript 中的價值。如今,那些寫 JSDoc 的人實際上就是 TypeScript 使用者,就像使用 --strict 的人一樣。
1.2 TypeScript 的競争者有哪些?
TypeScript 的目标是為人們提供編寫大型 JavaScript 項目并對後期維護有信心的工具。JavaScript 本身沒有的文法支援表示每個辨別符的類型,除非運作 JavaScript 并在運作時進行檢測。為了解決這個問題,TypeScript 添加了額外的文法。
是以,如果說我們的目标是作為工具提供支援,那麼在這個領域有少數幾個競争者是 TypeScript 無法與之競争的:
- ESLint 和 TSLint:與 TypeScript 的定位相同,它們都是用來突出代碼中可能出現的錯誤,隻是沒有為檢查過程添加新的文法。兩者都不打算作為 IDE 內建的工具運作,而且 TS 和 TS/ESLint 經常會說那些對項目沒有意義的特性“是對方的領域”。在現代代碼中,TS/ESLint 的存在使得 TypeScript 可以做更少的檢查,這些檢查并不适用于所有代碼庫。雖然有一些功能重疊了,但可以把它們作為很好的補充工具。
- CoffeeScript:嘿,TypeScript 是 2012 年釋出的!CoffeeScript 和 TypeScript 的差別在于 CoffeeScript 想要改進 JavaScript 語言,比如給 JavaScript 添加一些特性。這意味着要了解 CoffeeScript 與其輸出的 JavaScript 的差別。随着時間推移,CoffeeScript 的最佳理念反而将其變成了另一個 JavaScript,人們為幾乎成為了 JavaScript 的 CoffeeScript 感到困擾。
- Flow:這是 Facebook 的 JavaScript 類型檢查工具和 IDE 工具語言。就像 TypeScript 一樣,Flow 為 JavaScript 添加了一些額外的文法支援,讓你擁有了一個更加豐富的類型系統,然後在編譯時再将其删除。當我剛開始寫 JavaScript 時,Flow 是我最先使用的工具,因為它更接近标準的 JavaScript。Flow 是一個很棒的類型系統,它與 TypeScript 有着不同的目标。任何看不見的類型層系統都必須不斷做出“正确”或者“感覺足夠正确”的決定,Flow 的目标是“正确”(譯者注: Flow 偏向于 soundness,在類型判斷中更加悲觀),而 TypeScript 的目标是“感覺上大部分情況都是正确的”(譯者注:而 TS 官方聲稱 TS 不是類型完備的,允許 unsound 行為,偏向于 completeness,在類型判斷中更加樂觀)。魚和熊掌不可兼得,完備的類型推導、良好的開發體驗和完美的 JS 協同(Perfect JavaScript Interop)隻能取其二。
那麼,為什麼大多數開源 Flow 代碼庫最終都遷移到了 TypeScript 呢?在我看來,很大程度上是由兩個團隊不同的側重點決定。Flow 是為了維護 Facebook 的代碼庫而建立的,而 TypeScript 是作為一種獨立的語言建立的。這裡有兩個證據可以證明:
- Facebook 的代碼庫是一個不能被分割的巨大的 monorepo,而 Flow 團隊為了使類型運作在這樣的大代碼庫下做了大量令人難以置信的工作。另一方面,TypeScript 可以說是“為建構小代碼庫服務(use projects to make sets of smaller codebases)”,因為這符合人們在開源社群中編寫 JavaScript 子產品的方式。我認為這麼說很合理,TypeScript 不能像 Flow 一樣運作在 Facebook 的代碼庫上,它要麼需要大量重寫 Facebook 的代碼來建構項目,要麼需要對 TypeScript 進行大量修改,這可能會影響到 TypeScript 整體開發者的體驗。
- 對比 DefinitelyTyped 和 Flow 對類型的做法,TypeScript 團隊會輪值一名編譯器工程師為 DefinitelyTyped 支援我們的建構工具,并幫助管理社群。而 Flow,它幾乎完全由社群維護。DT 現在規模更大了,因為它們一直緻力于非 Facebook 代碼的開發,這将很難獲得 Flow 團隊的資金支援。
微軟給 TypeScript 在内部創造的獨立環境讓它可以自由專注于工具開發和整個生态系統的維護,而不是隻專注于解決某個特别困難的問題。這讓 TypeScript 團隊能夠與許多人合作,不斷釋出社群想要的功能。随着時間的推移,我猜想因為外部的需求增長放緩,Flow 團隊越來越難為社群工作配置設定時間。這就形成了一個惡性循環。這使得 Flow 今天不再是 TypeScript 的直接“競争者”,而是一個關于如何從不同的角度,使用不同的限制去解決類似的問題的有趣視角。
2. 未來
2.1 對 TypeScript 的未來怎麼看?
目前阻礙人們使用 TypeScript 的最大障礙是它需要建構工具。我認為類型文法不太可能被加入 JavaScript 中,但是在 JavaScript 中“用注釋的方式定義類型”的可能性非常大。
這個想法是為 TypeScript 這樣的類型系統建立一套文法,但是不定義 JS 運作時會發生什麼。
const a: string = "1234"
// 将會變成這樣
const a/_: string _/ = "1234"
// 傳入 JS 引擎
在這個例子中,JS 引擎會知道 : string 是一個類型注釋,在 = 處結尾。這實際的工作方式是複雜的,需要時間來弄清楚。然而,讓 TypeScript 能在 JavaScript 中“原生地”運作将降低它被使用的障礙。它會像 Babel 添加 TypeScript 支援時一樣對 TypeScript 施加一些限制。但我覺得這是值得的。
Deno 是一個消除所有 TS 障礙的關鍵例子,它通過運作一個 Rust 編寫的工具,能夠非常快速地将 TS 編譯到 JS,模拟了目前 JavaScript 引擎對原生 TypeScript 的支援。
2.2 如今的競争者
- JetBrins WebStorm - 這是一個有進階 JavaScript 工具支援的 IDE。他們有自己的引擎用于重構、代碼流分析并對 JavaScript 文法進行檢查。這很好,JetBrains 在他們所有的 IDE 上都做了紮實的工作。我過去經常使用 AppCode 處理 iOS 的工作。當你有一個 TypeScript 項目時,WebStorm 會将 TypeScript 的語言工具和自己的工具混合在一起,這對你來說是雙赢的。
- 編譯到 JS 的語言 - 目前的例子有 Elm,ReScript,KotlinScript,這些語言的核心目标是與 JavaScript 互動。對于 TypeScript 來說,這些都是很有趣的語言,它們有一個幹淨的環境來實作類型系統 —— 也就是,沒有 JS 包袱。作為競争對手,它們傾向于更細分的市場,因為它們的核心不是 JavaScript ,并且社群也被從 CoffeeScript 遷移所困擾過。
- WASM - 我聽到 WASM 作為 TypeScript 競争者的觀點是,WASM 可以作為語言取代 JS 控制浏覽器 DOM。反對這一觀點的人普遍認為,WASM 沒有 DOM 綁定,而且可能永遠不會有。TypeScript 包含了 JavaScript 的缺點,如果你在 JavaScript 運作時中加入過 WASM 的話,你幾乎總是會更加喜歡它。也就是說,AssemblyScript 在這方面做了一些很好的工作。也許把 WASM 想成 JSON 會更好,它是另一個組成項目的工具,不太可能成為 JavaScript 的競争者,除非 WASM 和 DOM 的互動方式有所改變。
- 編譯到 WASM 的語言 - 比如 Rust,Go,Swift,等其它可以編譯到 WASM 的語言。這些語言都可能占據 TypeScript 目前作為工具和 web 核心構模組化塊的位置,不過世事難料,誰知道會怎麼樣呢?這些語言能夠提供各種不同的基本類型,并且基于不同的目标從頭建構。如果 WASM 和 WASI 最終獲得成功,那麼我認為将會與平台相關(想想 apps 等功能實作),看看它們的發展方向會很有趣。說心裡話,它們不會是 TypeScript 的競争者,而是 JavaScript 的。
2.3 TypeScript 怎麼看它在生态中的位置?
TypeScript 希望在類型系統和編輯器工具領域進行創新。我們擁有在主流程式設計語言中表達能力最強的類型系統之一。
TypeScript 最初被建立時,對 JavaScript 進行修改的流程和現在非常不同,是以 TypeScript 中有一些特性實際上是 TC39 的領域,但仍然需要向後相容。這些特性可能在 JavaScript 中存在很多年,并且經過了多次疊代,這意味着 TypeScript 必須維護一個特定語言特性的兩種版本。
是以我們的目标是成為 TC39 JavaScript 語言委員會的優秀成員,就編輯器支援的語言特性進行回報,支援 TypeScript 使用者想要看到的特性。通過這種協作方式,TC39 控制了 JavaScript,TypeScript 也支援他們。
2.4 TypeScript 怎麼看它的閱聽人?
TypeScript 的閱聽人主要有:
- JavaScript 使用者(作為語言工具)
- JS + JSDoc 使用者(作為語言工具)
- TypeScript 使用者(作為編譯器,語言工具)
- TypeScript 嚴格模式(作為編譯器,語言工具)
雖然項目使用 babel / swc / sucrase / esbuild 等工具建構時,tsc 是可選的,但是上面的幾種閱聽人仍然可以在每次或至少每兩次 TS 版本釋出中獲得新特性(譯者注:babel、esbuild 等會更新支援 TS 新特性。可能是 TS 團隊直接去這些項目裡做,也可能會在沒有 tsc 的情況下為這些項目提供特性,比如通過 vscode。在 TS roadmap 中可以了解更多釋出計劃)。
2.5 TypeScript 是如何跟蹤 JS 生态的?
團隊從以下幾個方式聽取回報:
- GitHub issues 有持續不斷的評論洪流
- 微軟内部團隊要求提供特性,或者要求我們幫忙調試他們緩慢的代碼庫
- 通過 Gitter 或者 TypeScript 社群的 Discord 與社群建立聯系
- 通過微團的内部工具對想法 / 設計進行使用者測試
- 與 VS Code 有着非常緊密的聯系,許多語言工具的回報都來自于他們
- 我們會閱讀每一條 @ TypeScript 團隊的推特
- 我們會跟蹤遷移到 TypeScript 和從 TypeScript 遷走的部落格文章
- 我們會跟蹤行業調查和程式設計語言概述
位元組跳動資料平台前端團隊,在公司内負責大資料相關産品的研發。我們在前端技術上保持着非常強的熱情,除了資料産品相關的研發外,在資料可視化、海量資料處理優化、web excel、WebIDE、私有化部署、工程工具都方面都有很多的探索和積累,有興趣可以與我們聯系。