值固定讓存量工作區(qū)平穩(wěn)編譯)
Nx 23.1.0 TypeScript 6 遷移指南用 ignoreDeprecations 與默認(rèn)值固定讓存量工作區(qū)平穩(wěn)編譯【免費(fèi)下載鏈接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/nx/nxTypeScript 6 將多項(xiàng)長期廢棄的編譯選項(xiàng)升級為硬錯誤并翻轉(zhuǎn)了若干選項(xiàng)的默認(rèn)值直接升級會讓大量存量 Nx 工作區(qū)立刻編譯失敗。本文基于 Nx 倉庫中的官方遷移文檔 add-ignore-deprecations-for-ts6.md結(jié)合 遷移源碼 與其 測試用例完整講解 Nx 23.1.0 提供的23-1-0-add-ignore-deprecations-for-ts6自動遷移它如何寫入ignoreDeprecations: 6.0、如何為四條被翻轉(zhuǎn)的默認(rèn)值做固定pin、又如何在不改動共享 base 配置的前提下通過extends繼承語義精準(zhǔn)落筆。讀完你將掌握 TypeScript 6 下這些破壞性變化的具體表現(xiàn)以及 Nx 遷移機(jī)制如何在不改變項(xiàng)目語義的前提下保住編譯與運(yùn)行行為。TypeScript 6 帶來的四類破壞性變化要理解這次遷移的價值先看清 TypeScript 6 相對 TypeScript 5 到底改了什么。遷移文檔與源碼DEFAULT_PRESERVING_PINS常量的注釋把破壞點(diǎn)歸結(jié)為四類這也是遷移需要固定的四條默認(rèn)值編譯選項(xiàng)TypeScript 5 行為TypeScript 6 行為不處理的結(jié)果strict未設(shè)置時視為false未設(shè)置時視為true原本非嚴(yán)格模式的項(xiàng)目突然進(jìn)入嚴(yán)格模式出現(xiàn)大量類型錯誤noUncheckedSideEffectImports默認(rèn)false默認(rèn)true裸副作用導(dǎo)入如import ./styles.css若沒有對應(yīng)的 ambient 模塊聲明會變成硬錯誤TS2882types未設(shè)置時自動加載全部types包未設(shè)置時不再自動加載依賴隱式加載全部 types的配置典型如 ts-node 編譯jest.config.ts時依賴types/node會丟失類型esModuleInterop默認(rèn)false默認(rèn)翻轉(zhuǎn)為trueimport * as x from cjs綁定到不可調(diào)用的命名空間對象調(diào)用或new該導(dǎo)入會在運(yùn)行時失敗其中esModuleInterop: false本身在 TypeScript 6 里就是一個廢棄值將在 TypeScript 7 中被移除所以它既要被固定下來保持舊行為又要靠ignoreDeprecations壓制它觸發(fā)的廢棄告警——遷移把這兩件事放在同一次運(yùn)行里完成。廢棄值升級為硬錯誤的完整清單遷移源碼中的hasDeprecatedOption函數(shù)add-ignore-deprecations-for-ts6.ts給出了被判定為TypeScript 6 廢棄值的完整清單與文檔列出的示例一一對應(yīng)moduleResolution設(shè)置為node/node10/classic設(shè)置了baseUrltarget設(shè)置為es5esModuleInterop: false設(shè)置了outFilemodule設(shè)置為amd/umd/system/nonealwaysStrict: falseallowSyntheticDefaultImports: false設(shè)置了downlevelIteration無論真假。這些值在 TypeScript 5.8 上還能靜默編譯到了 TypeScript 6.0 就會變成TS5101/TS5107一類的硬廢棄錯誤。測試用例 detectedCases 逐一驗(yàn)證了這些值的檢測邏輯。遷移的整體設(shè)計(jì)與執(zhí)行時機(jī)23-1-0-add-ignore-deprecations-for-ts6在 Nx 的遷移清單中注冊于版本23.1.0-beta.8并帶有一個關(guān)鍵門控條件migrations.json{ 23-1-0-add-ignore-deprecations-for-ts6: { version: 23.1.0-beta.8, description: ……, requires: { typescript: 6.0.0 }, factory: ./dist/src/migrations/update-23-1-0/add-ignore-deprecations-for-ts6, documentation: ./dist/src/migrations/update-23-1-0/add-ignore-deprecations-for-ts6.md } }也就是說該遷移只在工作區(qū)實(shí)際運(yùn)行 TypeScript 6 時才會執(zhí)行requires條件不滿足會自動跳過并且在每個命中的tsconfig*.json上以兩遍掃描的方式工作源碼中的 Pass 1 / Pass 2 注釋Pass 1寫固定與加載標(biāo)志為所有鏈根沒有extends的配置文件補(bǔ)齊缺失的四條默認(rèn)值同時為每個文件名恰好為tsconfig.json的文件寫入配置加載標(biāo)志。之所以要先寫是因?yàn)?Pass 2 的extends解析要讀到這些剛落盤的內(nèi)容。Pass 2壓制廢棄值讓 TypeScript 自己解析每個配置文件extends合并后的有效選項(xiàng)凡命中上述廢棄值清單且有效ignoreDeprecations不是6.0的就補(bǔ)上該標(biāo)志。兩遍都通過globAsync(tree, [**/tsconfig*.json])收集文件最終統(tǒng)一調(diào)用formatFiles(tree)格式化。樹友好的解析宿主讓 extends 解析看得見待寫入內(nèi)容Pass 2 調(diào)用 TypeScript 的parseJsonConfigFileContent時傳入的是createTreeParseConfigHost(tree)生成的宿主ts-config.ts。它的關(guān)鍵作用有兩點(diǎn)基于 Nx 內(nèi)存樹Tree回答文件存在性與內(nèi)容讀取因此能解析到 Pass 1 剛寫入、尚未落盤的修改也能解析到node_modules中包提供的 base 配置naive 的tree.read宿主做不到后者readDirectory被實(shí)現(xiàn)為空操作遷移只關(guān)心合并后的compilerOptions不需要掃描源文件列表從而大幅減少解析開銷。測試用例中專門覆蓋了多種extends形式普通相對路徑、無擴(kuò)展名的./base、數(shù)組形式[./base-a.json, ./base-b.json]TypeScript 從左到右合并、后者覆蓋前者、包形式tsconfig/base/base.json。在 flags a child that inherits a deprecated value through an array-form extends 用例中base-a是干凈的bundler解析而base-b的node10覆蓋了它合并結(jié)果帶廢棄值子配置因此必須自帶標(biāo)志——兩個 base 都不是tsconfig*.json命名永遠(yuǎn)不會被收集和編輯。三條寫入規(guī)則詳解規(guī)則一為鏈根固定四條 TypeScript 6 被翻轉(zhuǎn)的默認(rèn)值DEFAULT_PRESERVING_PINSadd-ignore-deprecations-for-ts6.ts定義了四組要固定到鏈根own.extends undefined上的鍵值const DEFAULT_PRESERVING_PINS: ReadonlyArray[string, boolean | string[]] [ [strict, false], [noUncheckedSideEffectImports, false], [types, [*]], [esModuleInterop, false], ];寫入遵循缺失才補(bǔ)原則只有鏈根上沒有顯式設(shè)置該鍵時才寫入用戶顯式配置過的值無論strict: true還是strict: false一律不動。這一點(diǎn)被測試明確鎖定does not overwrite an explicit strict true顯式strict: true保持不變does not overwrite an explicit types list顯式types: [node]保持不變does not overwrite an explicit empty types array顯式types: []有意退出也保持不變keeps an explicit esModuleInterop truetrue是 TS6 的新默認(rèn)自然不覆蓋。types: [*]的通配符寫法用于恢復(fù) TypeScript 5 的自動加載全部 types行為——這是 ts-node 對jest.config.ts做類型檢查時能找到types/node的關(guān)鍵。而esModuleInterop: false由于本身已廢棄Pass 2 會在同一次運(yùn)行中為它補(bǔ)上ignoreDeprecations將其靜默源碼注釋明確說明這個 false 固定要先于廢棄壓制執(zhí)行so the added false is silenced in the same run。規(guī)則二為所有 tsconfig.json 寫入配置加載標(biāo)志與只針對鏈根、只針對命中廢棄值的其他寫入不同文件名恰好為tsconfig.json的文件無條件寫入ignoreDeprecations: 6.0。原因在文檔和源碼中解釋得很清楚jest 與 ts-node 編譯配置文件如jest.config.ts時會從工作目錄向上查找并自動加載名為tsconfig.json的文件注意不是從配置文件自身所在目錄查找ts-node 在配置未設(shè)置target時會注入默認(rèn)值target: es5而es5正是 TypeScript 6 的廢棄值TS5107哪怕這個tsconfig.json本身干干凈凈也會在加載時報錯該標(biāo)志會被 ts-node 透傳從而保住這次配置加載在沒有任何實(shí)際廢棄值時它是惰性的、無副作用的。測試always flags a clean chain-root tsconfig.json but not a clean tsconfig.base.json精確驗(yàn)證了這條只認(rèn)名字的規(guī)則干凈的tsconfig.json獲得標(biāo)志而干凈的tsconfig.base.json不是自動加載目標(biāo)保持原樣。規(guī)則三基于合并后的有效選項(xiàng)決定是否補(bǔ)標(biāo)志Pass 2 的判定不是看文件自身寫了什么而是看TypeScript 合并extends鏈之后的有效選項(xiàng)。這帶來三個精妙的行為繼承到廢棄值也補(bǔ)子配置從遷移不會編輯的 base如包提供的tsconfig/*預(yù)設(shè)、非tsconfig*.json命名的base.json繼承到node10時子配置會自帶標(biāo)志。測試用例 flags a child whose deprecated value comes from a non-tsconfig-named base 驗(yàn)證了這一點(diǎn)base 保持原樣子配置直接補(bǔ)標(biāo)志。已經(jīng)繼承到6.0就不重復(fù)寫如果子配置通過extends已經(jīng)獲得了有效的6.0則不再冗余寫入。測試 does not re-flag a descendant that inherits the flag from tsconfig.json 驗(yàn)證tsconfig.spec.json繼承自已帶標(biāo)志的tsconfig.json即使它自己寫了moduleResolution: node10也不會重復(fù)補(bǔ)標(biāo)志。過期的本地5.0會被升級為6.0一個本地ignoreDeprecations: 5.0會覆蓋繼承來的6.0導(dǎo)致廢棄錯誤仍然爆發(fā)因此必須升級。測試用例 upgrades a stale local flag that overrides an inherited 6.0 與 upgrades a stale local flag when the deprecated value is inherited 覆蓋了本地值廢棄與繼承值廢棄兩種方向。此外ts-node.compilerOptions這個 overlay 塊被單獨(dú)檢查tsc不會合并它主塊剛寫入的6.0也不一定能可靠地傳導(dǎo)到 ts-node 的運(yùn)行時覆蓋層不同 ts-node 版本行為不一所以只要 overlay 自身或解析后的主配置帶有廢棄值就直接給 overlay 單獨(dú)補(bǔ)標(biāo)志。測試 adds ignoreDeprecations to a ts-node.compilerOptions block 與 adds ignoreDeprecations to both compilerOptions and ts-node block 分別驗(yàn)證了兩種組合。兩類被有意跳過的文件遷移文檔明確列出了兩類不參與固定、但可能仍參與標(biāo)志寫入的文件使用extends的文件它們從鏈根繼承四條固定值因此不重復(fù)固定。測試does not touch strict on a file that has extends驗(yàn)證strict不會被寫入這種文件。純 solution 式容器files: []且沒有include它們不選擇任何源文件不獲得四條固定但名字恰好是tsconfig.json的 solution 容器仍會獲得配置加載標(biāo)志jest/ts-node 加載場景與選源無關(guān)。測試 adds only the config-load flag to a solution-container tsconfig.json 驗(yàn)證輸入{ files: [] }后輸出{ compilerOptions: { ignoreDeprecations: 6.0 } }。還有一個防御性細(xì)節(jié)如果compilerOptions字段存在但不是對象例如[]遷移會原樣跳過該文件避免modify()拋異常中斷整場遷移——測試 leaves a non-object compilerOptions untouched without crashing 與 leaves a non-object compilerOptions untouched even when it inherits a deprecated value 覆蓋了這兩種情況。邊界情況與失敗處理extends 解析失敗時顯式告警Pass 2 使用extendsResolutionFailedts-config.ts檢查解析結(jié)果如果某個 base 既不在樹中、tsc也讀不到合并選項(xiàng)就是不完整的從該 base 繼承的廢棄值可能被漏掉。此時遷移不猜測、不靜默而是輸出logger.warn提示該配置文件本身無法編譯需要先修復(fù)。這是寧可顯式暴露、不可偷偷修復(fù)的設(shè)計(jì)取舍。冪等性遷移對所有場景都保證冪等第二次運(yùn)行不會產(chǎn)生任何額外修改。測試中is idempotent與is idempotent for the strict-pin pass分別驗(yàn)證了整體與單遍的冪等性。此外遷移基于jsonc-parser的parseTree/modify/applyEdits做結(jié)構(gòu)化編輯并保留注釋與格式keepLines: true, insertSpaces: true, tabSize: 2測試 preserves comments when adding the flag 驗(yàn)證了注釋得以保留。一次典型遷移的完整效果文檔給出了最直觀的示例。一個鏈根tsconfig.json修改前{ compilerOptions: { target: es5, module: esnext, moduleResolution: bundler } }遷移后{6-10}標(biāo)出的是新增行{ compilerOptions: { target: es5, module: esnext, moduleResolution: bundler, strict: false, noUncheckedSideEffectImports: false, types: [*], esModuleInterop: false, ignoreDeprecations: 6.0 } }觀察這個結(jié)果可以完整印證前文所有規(guī)則target: es5是廢棄值故補(bǔ)ignoreDeprecations: 6.0strict、noUncheckedSideEffectImports、types、esModuleInterop四條被固定以保留 TS5 語義esModuleInterop: false本身的廢棄告警被同一次寫入的6.0靜默。遷移完成后Nx 會輸出三條logger.info摘要分別統(tǒng)計(jì)獲得加載標(biāo)志的tsconfig.json數(shù)量、補(bǔ)上廢棄壓制標(biāo)志的文件數(shù)量、以及完成默認(rèn)值固定的鏈根數(shù)量見 add-ignore-deprecations-for-ts6.ts。與同批遷移的配合rootDir 固定TypeScript 6 的破壞性變化不止默認(rèn)值翻轉(zhuǎn)rootDir的推斷規(guī)則也變了TS5 推斷為程序非聲明輸入文件的公共目錄TS6 改為配置文件自身所在目錄導(dǎo)致 spec/e2e 配置通過paths別名導(dǎo)入其他項(xiàng)目源碼時出現(xiàn)TS5011/TS6059。Nx 23.1.0 在同一版本提供了配套遷移23-1-0-set-tsconfig-root-dir-for-ts6見 set-tsconfig-root-dir-for-ts6.md同樣requiresTypeScript6.0.0把rootDir固定到 TS5 推斷出的值。升級 TypeScript 6 時兩條遷移配合執(zhí)行才能完整保住編譯與產(chǎn)物布局。適用前提與限制該遷移僅在工作區(qū) TypeScript 版本滿足6.0.0時執(zhí)行且屬于 Nx 23.1.0 及后續(xù)版本的能力在 TypeScript 5.x 工作區(qū)中運(yùn)行不會觸發(fā)。遷移的定位是在不遷移到完整 TypeScript 6 配置的前提下讓存量工作區(qū)繼續(xù)編譯它通過壓制廢棄告警換取兼容不能消除module/moduleResolution組合錯誤TS5110例如 ts-node 強(qiáng)制module: commonjs撞上繼承來的nodenext解析也不處理noUncheckedSideEffectImports: true帶來的語義診斷TS2882這是語義錯誤而非廢棄錯誤ignoreDeprecations無法壓制。esModuleInterop: false的固定只是把互操作語義變化推遲到 TypeScript 7 的遷移屬于有意的漸進(jìn)式策略而非永久方案升級到 TS7 時仍需處理import * as x from cjs的調(diào)用語義變化。如果你想在本地驗(yàn)證或深入這些行為可以直接閱讀 遷移源碼 與 1100 余行的 測試套件測試覆蓋了本文提到的每一條規(guī)則與邊界情況是理解 TypeScript 6 破壞性變更的最佳實(shí)操教材。【免費(fèi)下載鏈接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/nx/nx創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考