ts-migrate:Airbnb進行大規模TypeScript遷移的神器

引言

業界對於TypeScript已是政治正確的選擇了,愈來愈多的前端庫/框架均採用TS,例如Ant Design 4.0、Vue 3.0等。同時,TS也提供了更健全的語法能力、靜態的代碼類型檢查、更友好的代碼輔助提示等等。前端

早在2019年,Airbnb就已經在JSConf上分享了其要對現有前端項目進行大規模TypeScript遷移轉換的計劃。可是對於現有項目進行TypeScript轉換並非一件輕鬆,Airbnb總共有超過200萬行代碼,超過100個內部npm包。react

遷移策略

大規模代碼遷移每每是一項很是複雜的工做,Airbnb探索過幾個遷移策略:git

  • 混合遷移策略:一個個文件進行遷移,修復類型錯誤。allowJS配置能夠容許JS和TS文件在一個項目中並存。使用混合遷移策略能夠不用暫停當前開發,逐步的一個個文件進行遷移。可是,對於研發人員須要更長的適應和遷移時間。github

  • 徹底遷移策略:一次性實現文件徹底遷移。咱們會使用any或者@ts-ignore註釋幫助項目編譯,隨後會補充更加具體的類型申明。typescript

採用徹底遷移策略有不少的好處:npm

  • 項目的一致性:在徹底遷移策略下,項目使用使用TypeScript來編寫,所以,開發者並不須要在JS和TS之間進行切換。json

  • 修復類型比修復文件容易:修復一個完整文件很是複雜,由於它可能有不少外部依賴,所以,採用混合遷移的方式,很難保證遷移的進度和狀態。前端框架

所以,Airbnb採用了徹底遷移策略。然而,一次性遷移完整項目難度很是大。Airbnb研發了一個轉換工具:ts-migrate,在初試轉換過程當中,儘量實現類型的自動轉換。微信

固然,這個工具並不能保證明現徹底沒有錯誤的轉換,可是在實際使用過程當中,對於一個超過50000行代碼、1000個文件的項目,從JavaScript轉換到TypeScript使用這個工具每每只須要1天!markdown

在Airbnb,React是主要前端框架,因此主要的codemods轉換都是基於React概念。ts-migrate也可能能夠適用於其餘框架或者三方庫。

遷移流程的步驟

讓咱們看下一個JavaScript項目轉換到TypeScript所須要的主要步驟。

  1. 首先是建立tsconfig.json文件。ts-migrate會提供一個默認基礎配置文件,下面是個例子:
{
  "extends": "../typescript/tsconfig.base.json",
  "include": [".", "../typescript/types"]
}
複製代碼
  1. 下一步是將代碼後綴從.js/.jsx轉換成.ts/.tsx,自動化實現這步其實很是簡單。

  2. 第三步是執行codemod,ts-migrate經過plugins方式來組織代碼轉換的能力。經過AST解析工具,咱們將原有的JS代碼,進行解析,分析類型,添加聲明,而後生成相應的TS代碼。

ts-migrate概覽

ts-migrate有三個部分組成,而且開源在github.com/airbnb/ts-m…

  • ts-migrate
  • ts-migrate-server
  • ts-migrate-plugins

轉換工具並不能將一個項目徹底遷移完成,可是,它會經過ignore的註釋讓編譯器忽略錯誤,從而可讓項目儘量運行起來,隨後你能夠再逐步修復錯誤問題。

遷移的總體過程以下:

  • 解析tsconfig.json
  • 建立.ts源代碼文件
  • 把每個文件都放到TS Server進行診斷,包括三種類型的診斷:semanticDiagnostics(語義診斷),syntacticDiagnostics(句法診斷)和suggestionDiagnostics(建議診斷)。經過這些診斷,工具會找到須要修改的代碼內容,而且進行標記。
  • 對每個文件執行plugin。plugin會對源代碼進行修改,而且通知TS Server。

通用性Plugin

plugin都會放在ts-migrate-plugins目錄下面,咱們能夠先看下兩個插件:explicitAnyPlugin和declareMissingClassPropertiesPlugin。

exlicitAnyPlugin主要會對全部文件中的語義診斷錯誤進行處理。對於沒法推導類型的變量添加any,能夠幫助解決編譯問題。

遷移前:

const fn2 = function(p3, p4) {}
const var1 = [];
複製代碼

遷移後:

const fn2 = function(p3: any, p4: any) {}
const var1: any = [];
複製代碼

declareMissingClassPropertiesPlugin一樣也會找到類申明中缺失的類型,而且添加any修飾。

React相關的插件

reactPropsPlugin能夠將PropTypes的類型信息轉換成TypeScript的props類型申明。這個插件會在.tsx文件中執行,reactPropsPlugin會尋找全部PropTypes的定義,經過AST進行解析,而且將其轉換成一個新的props申明:type Props = {...}。

React中的state和生命週期很是常見,咱們經過兩個插件來處理。

若是一個組件是有狀態的,reactClassStatePlugin會生成一個新的語句: type State = any; reactClassLifecycleMethodsPlugin會給生命週期方法提供相應的類型申明。

整個工具依舊有很大的改進空間,可是,能夠做爲TypeScript的初始工具,很好的幫你開始整個遷移過程。這個工具並不支持hooks,由於Airbnb原始項目使用的是老版本的React。

確保項目編譯成功

咱們的目標是但願將項目進行基本類型轉換,同時不改變任何代碼行爲。

經過工具轉換可能會致使代碼lint檢查失敗,所以,可使用Prettier進行代碼自動格式化,經過ESLint確保代碼符合規範。

遷移過程的最後一步是保證全部TypeScript的編譯錯誤能夠發現而且修復。咱們會在這些地方插入@ts-ignore而且提供備註。

// @ts-ignore ts-migrate(7053) FIXME: No index signature with a parameter of type 'string...
const { field1, field2, field3 } = DATA[prop];
// @ts-ignore ts-migrate(2532) FIXME: Object is possibly 'undefined'.
const field2 = object.some_property;
複製代碼

工具也會支持JSX語法

{*
// @ts-ignore ts-migrate(2339) FIXME: Property 'NORMAL' does not exist on type 'typeof W... */}
<Text weight={WEIGHT.NORMAL}>
  some text
</Text>
<input
  id="input"
  // @ts-ignore ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'string'.
  name={getName()}
/>
複製代碼

有了這些註釋和提示,能夠很方便研發人員進行修改。這些註釋,也可以幫助咱們瞭解代碼的質量,而且發現潛在的代碼問題。

總結

Airbnb的TypeScript代碼轉換還在進行中,咱們依舊還有一些老舊代碼項目使用JavaScript,咱們也有很多$TSFixMe和@ts-ignore註釋須要修復。

使用ts-migrate工具極大的提高了咱們的遷移效率。研發人員更加關注於類型的修復。目前,咱們已經轉換了~86%的代碼,到年末咱們預期會達到95%。

-End-

關注**『奶爸碼農』**微信公衆號,從事互聯網研發工做10+年,經歷IBM、SAP、陸金所、攜程等國內外IT公司,目前在美團負責餐飲相關大前端技術團隊,按期分享關於大前端技術、投資理財、我的成長的思考與總結。

相關文章
相關標籤/搜索