專案引用

專案引用(Project references)允許你將 TypeScript 程式拆分為更小的部分,該功能從 TypeScript 3.0 開始提供。

透過這種方式,你可以大幅縮短構建時間,加強元件之間的邏輯分離,並以全新且更好的方式組織程式碼。

我們還為 tsc 引入了一種新模式,即 --build 標誌。它與專案引用配合使用,可以實現更快的 TypeScript 構建。

示例專案

讓我們看一個相當常見的程式,並瞭解專案引用如何幫助我們更好地組織它。假設你有一個包含兩個模組的專案:converterunits,並且每個模組都有對應的測試檔案。

/
├── src/
│ ├── converter.ts
│ └── units.ts
├── test/
│ ├── converter-tests.ts
│ └── units-tests.ts
└── tsconfig.json

測試檔案匯入實現檔案並執行一些測試。

ts
// converter-tests.ts
import * as converter from "../src/converter";
assert.areEqual(converter.celsiusToFahrenheit(0), 32);

以前,如果你使用單個 tsconfig 檔案,這種結構的處理會相當尷尬。

  • 實現檔案可能會匯入測試檔案。
  • 如果沒有讓 src 出現在輸出資料夾名稱中(你可能不希望這樣),則無法同時構建 testsrc
  • 僅僅更改實現檔案中的內部程式碼,就需要再次對測試進行型別檢查,儘管這根本不會導致新的錯誤。
  • 僅僅更改測試,就需要再次對實現進行型別檢查,即使沒有任何變化。

你可以使用多個 tsconfig 檔案來解決部分問題,但會出現新的問題。

  • 沒有內建的最新狀態檢查功能,所以你最終總是要執行兩次 tsc
  • 呼叫兩次 tsc 會導致更多的啟動時間開銷。
  • tsc -w 無法同時在多個配置檔案上執行。

專案引用可以解決所有這些問題以及更多其他問題。

什麼是專案引用?

tsconfig.json 檔案有一個新的頂層屬性 references。它是一個指定要引用的專案的物件陣列。

js
{
"compilerOptions": {
// The usual
},
"references": [
{ "path": "../src" }
]
}

每個引用的 path 屬性可以指向包含 tsconfig.json 檔案的目錄,或者指向配置檔案本身(配置檔案可以有任何名稱)。

當你引用一個專案時,會發生一些新的變化:

  • 從被引用的專案匯入模組時,將改為載入其輸出宣告檔案 (.d.ts)。
  • 如果被引用的專案生成了 outFile,則該輸出檔案的 .d.ts 宣告將在當前專案中可見。
  • 構建模式(見下文)會在需要時自動構建被引用的專案。

透過拆分為多個專案,你可以極大地提高型別檢查和編譯的速度,減少在編輯器中使用時的記憶體佔用,並加強對程式邏輯分組的強制執行。

composite

被引用的專案必須啟用新的 composite 設定。此設定是必需的,以確保 TypeScript 能夠快速確定在哪裡找到被引用專案的輸出。啟用 composite 標誌會帶來幾點變化:

  • 如果沒有顯式設定 rootDir,它將預設為包含 tsconfig 檔案的目錄。
  • 所有實現檔案必須由 include 模式匹配,或在 files 陣列中列出。如果違反此約束,tsc 會告知你哪些檔案未被指定。
  • 必須開啟 declaration

declarationMap

我們還增加了對 宣告原始碼對映(declaration source maps) 的支援。如果啟用 declarationMap,你將能夠在受支援的編輯器中使用“轉到定義(Go to Definition)”和重新命名等功能,透明地跨專案邊界導航和編輯程式碼。

專案引用的注意事項

專案引用有一些你需要了解的權衡點。

由於依賴專案使用的是由其依賴項構建的 .d.ts 檔案,你必須要麼將某些構建輸出提交到版本控制中,或者在克隆專案後先構建一次,然後才能在編輯器中導航專案而不會看到虛假的錯誤。

當使用 VS Code 時(自 TS 3.7 起),我們有一個後臺記憶體中的 .d.ts 生成過程,它應該能夠緩解此問題,但它會對效能產生一定影響。對於非常大的複合專案,你可能希望使用 disableSourceOfProjectReferenceRedirect 選項 將其停用。

此外,為了保持與現有構建工作流的相容性,除非使用 --build 開關呼叫,否則 tsc 不會自動構建依賴項。讓我們進一步瞭解 --build

TypeScript 的構建模式

一項期待已久的功能是 TypeScript 專案的智慧增量構建。在 3.0 中,你可以將 --build 標誌與 tsc 一起使用。這實際上是 tsc 的一個新的入口點,它的行為更像一個構建協調器,而不是一個簡單的編譯器。

執行 tsc --build(簡稱 tsc -b)將執行以下操作:

  • 找到所有引用的專案。
  • 檢測它們是否為最新狀態。
  • 按正確的順序構建過時的專案。

你可以為 tsc -b 提供多個配置檔案路徑(例如 tsc -b src test)。就像 tsc -p 一樣,如果配置檔名為 tsconfig.json,則無需指定檔名本身。

tsc -b 命令列

你可以指定任意數量的配置檔案。

shell
> tsc -b # Use the tsconfig.json in the current directory
> tsc -b src # Use src/tsconfig.json
> tsc -b foo/prd.tsconfig.json bar # Use foo/prd.tsconfig.json and bar/tsconfig.json

不必擔心在命令列上傳遞檔案的順序——如果需要,tsc 會對它們進行重新排序,以便始終優先構建依賴項。

還有一些特定於 tsc -b 的標誌:

  • --verbose:列印詳細日誌以解釋正在發生的事情(可與其他任何標誌組合使用)。
  • --dry:顯示將要執行的操作,但實際上不構建任何內容。
  • --clean:刪除指定專案的輸出(可與 --dry 組合使用)。
  • --force:表現得好像所有專案都已過時一樣。
  • --watch:監視模式(除了 --verbose 外,不能與其他任何標誌組合使用)。

注意事項

通常,在存在語法或型別錯誤時,tsc 仍會產生輸出(.js.d.ts),除非啟用了 noEmitOnError。在增量構建系統中這樣做非常糟糕——如果你的某個過時依賴項出現了新錯誤,你將只會看到一次,因為隨後的構建會跳過構建那個現在看起來“最新”的專案。因此,tsc -b 實際上表現為對所有專案啟用了 noEmitOnError

如果你提交了任何構建輸出(.js, .d.ts, .d.ts.map 等),根據你的原始碼控制工具是否在本地副本和遠端副本之間保留時間戳,你可能需要在某些原始碼控制操作之後執行 --force 構建。

MSBuild

如果你有一個 msbuild 專案,可以透過新增以下內容啟用構建模式:

xml
<TypeScriptBuildMode>true</TypeScriptBuildMode>

到你的 proj 檔案中。這將啟用自動增量構建以及清理。

請注意,與 tsconfig.json / -p 一樣,現有的 TypeScript 專案屬性將不會被遵循——所有設定都應使用你的 tsconfig 檔案進行管理。

一些團隊設定了基於 msbuild 的工作流,其中 tsconfig 檔案具有與它們配對的管理專案相同的隱式圖順序。如果你的解決方案是這樣的,你可以繼續將 msbuildtsc -p 結合專案引用一起使用;它們是完全可互操作的。

指南

總體結構

隨著 tsconfig.json 檔案數量的增加,你通常會想要使用 配置檔案繼承 來集中管理通用的編譯器選項。這樣,你只需在一個檔案中修改設定,而不必編輯多個檔案。

另一個好的做法是擁有一個“解決方案” tsconfig.json 檔案,它只需包含對所有葉子節點專案的 references,並將 files 設定為空陣列(否則解決方案檔案會導致檔案的重複編譯)。注意,從 3.0 開始,如果 tsconfig.json 檔案中至少有一個 reference,則擁有空的 files 陣列不再是錯誤。

這提供了一個簡單的入口點;例如,在 TypeScript 倉庫中,我們只需執行 tsc -b src 即可構建所有端點,因為我們在 src/tsconfig.json 中列出了所有子專案。

你可以在 TypeScript 倉庫中看到這些模式——請檢視 src/tsconfig-base.jsonsrc/tsconfig.jsonsrc/tsc/tsconfig.json 作為關鍵示例。

針對相對模組的結構組織

通常,轉換使用相對模組的倉庫不需要太多工作。只需在給定父資料夾的每個子目錄中放置一個 tsconfig.json 檔案,並新增對這些配置檔案的 reference,以匹配程式的預期分層。你需要將 outDir 設定為輸出資料夾的顯式子資料夾,或者將 rootDir 設定為所有專案資料夾的公共根目錄。

針對 outFiles 的結構組織

使用 outFile 的編譯佈局更靈活,因為相對路徑並不那麼重要。TypeScript 倉庫本身就是一個很好的參考——我們有一些“庫”專案和一些“端點”專案;“端點”專案保持得儘可能小,並且只引入它們需要的庫。

TypeScript 文件是一個開源專案。請 傳送 Pull Request 幫助我們改進這些頁面 ❤

此頁面的貢獻者
MHMohamed Hegazy (53)
OTOrta Therox (18)
RCRyan Cavanaugh (3)
TTheo (1)
MKMatt Kantor (1)
23+

最後更新:2026 年 3 月 27 日