路徑映射
有時模塊不是直接放在baseUrl下面兽肤。 比如,充分 "jquery"模塊地導(dǎo)入电禀,在運行時可能被解釋為"node_modules/jquery/dist/jquery.slim.min.js"笤休。 加載器使用映射配置來將模塊名映射到運行時的文件,查看 RequireJs documentation和SystemJS documentation葫松。
TypeScript編譯器通過使用tsconfig.json文件里的"paths"來支持這樣的聲明映射腋么。 下面是一個如何指定 jquery的"paths"的例子亥揖。
{
? "compilerOptions": {
? ? "baseUrl": ".", // This must be specified if "paths" is.
? ? "paths": {
? ? ? "jquery": ["node_modules/jquery/dist/jquery"] // 此處映射是相對于"baseUrl"
? ? }
? }
}
請注意"paths"是相對于"baseUrl"進行解析费变。 如果 "baseUrl"被設(shè)置成了除"."外的其它值,比如tsconfig.json所在的目錄扛稽,那么映射必須要做相應(yīng)的改變在张。 如果你在上例中設(shè)置了 "baseUrl": "./src"矮慕,那么jquery應(yīng)該映射到"../node_modules/jquery/dist/jquery"。
通過"paths"我們還可以指定復(fù)雜的映射瘟斜,包括指定多個回退位置螺句。 假設(shè)在一個工程配置里橡类,有一些模塊位于一處猫态,而其它的則在另個的位置。 構(gòu)建過程會將它們集中至一處勇凭。 工程結(jié)構(gòu)可能如下:
projectRoot
├── folder1
│? ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│? └── file2.ts
├── generated
│? ├── folder1
│? └── folder2
│? ? ? └── file3.ts
└── tsconfig.json
相應(yīng)的tsconfig.json文件如下:
{
? "compilerOptions": {
? ? "baseUrl": ".",
? ? "paths": {
? ? ? "*": [
? ? ? ? "*",
? ? ? ? "generated/*"
? ? ? ]
? ? }
? }
}
它告訴編譯器所有匹配"*"(所有的值)模式的模塊導(dǎo)入會在以下兩個位置查找:
"*": 表示名字不發(fā)生改變虾标,所以映射為<moduleName> => <baseUrl>/<moduleName>
"generated/*"表示模塊名添加了“generated”前綴璧函,所以映射為<moduleName> => <baseUrl>/generated/<moduleName>
按照這個邏輯基显,編譯器將會如下嘗試解析這兩個導(dǎo)入:
導(dǎo)入'folder1/file2'
匹配'*'模式且通配符捕獲到整個名字。
嘗試列表里的第一個替換:'*' -> folder1/file2库继。
替換結(jié)果為非相對名 - 與baseUrl合并 -> projectRoot/folder1/file2.ts宪萄。
文件存在榨惰。完成琅催。
導(dǎo)入'folder2/file3'
匹配'*'模式且通配符捕獲到整個名字。
嘗試列表里的第一個替換:'*' -> folder2/file3排监。
替換結(jié)果為非相對名 - 與baseUrl合并 -> projectRoot/folder2/file3.ts舆床。
文件不存在嫁佳,跳到第二個替換挨队。
第二個替換:'generated/*' -> generated/folder2/file3。
替換結(jié)果為非相對名 - 與baseUrl合并 -> projectRoot/generated/folder2/file3.ts蒿往。
文件存在盛垦。完成。
利用rootDirs指定虛擬目錄
有時多個目錄下的工程源文件在編譯時會進行合并放在某個輸出目錄下瓤漏。 這可以看做一些源目錄創(chuàng)建了一個“虛擬”目錄腾夯。
利用rootDirs颊埃,可以告訴編譯器生成這個虛擬目錄的roots; 因此編譯器可以在“虛擬”目錄下解析相對模塊導(dǎo)入蝶俱,就 好像它們被合并在了一起一樣班利。
比如榨呆,有下面的工程結(jié)構(gòu):
src
└── views
? ? └── view1.ts (imports './template1')
? ? └── view2.ts
generated
└── templates
? ? ? ? └── views
? ? ? ? ? ? └── template1.ts (imports './view2')
src/views里的文件是用于控制UI的用戶代碼罗标。 generated/templates是UI模版,在構(gòu)建時通過模版生成器自動生成积蜻。 構(gòu)建中的一步會將 /src/views和/generated/templates/views的輸出拷貝到同一個目錄下闯割。 在運行時,視圖可以假設(shè)它的模版與它同在一個目錄下竿拆,因此可以使用相對導(dǎo)入 "./template"宙拉。
可以使用"rootDirs"來告訴編譯器。 "rootDirs"指定了一個roots列表如输,列表里的內(nèi)容會在運行時被合并鼓黔。 因此,針對這個例子不见, tsconfig.json如下:
{
? "compilerOptions": {
? ? "rootDirs": [
? ? ? "src/views",
? ? ? "generated/templates/views"
? ? ]
? }
}
每當編譯器在某一rootDirs的子目錄下發(fā)現(xiàn)了相對模塊導(dǎo)入澳化,它就會嘗試從每一個rootDirs中導(dǎo)入。
rootDirs的靈活性不僅僅局限于其指定了要在邏輯上合并的物理目錄列表稳吮。它提供的數(shù)組可以包含任意數(shù)量的任何名字的目錄缎谷,不論它們是否存在。這允許編譯器以類型安全的方式處理復(fù)雜捆綁(bundles)和運行時的特性灶似,比如條件引入和工程特定的加載器插件列林。
設(shè)想這樣一個國際化的場景,構(gòu)建工具自動插入特定的路徑記號來生成針對不同區(qū)域的捆綁酪惭,比如將#{locale}做為相對模塊路徑./#{locale}/messages的一部分希痴。在這個假定的設(shè)置下,工具會枚舉支持的區(qū)域春感,將抽像的路徑映射成./zh/messages砌创,./de/messages等。
假設(shè)每個模塊都會導(dǎo)出一個字符串的數(shù)組鲫懒。比如./zh/messages可能包含:
export default [
? ? "您好嗎",
? ? "很高興認識你"
];
利用rootDirs我們可以讓編譯器了解這個映射關(guān)系嫩实,從而也允許編譯器能夠安全地解析./#{locale}/messages,就算這個目錄永遠都不存在窥岩。比如甲献,使用下面的tsconfig.json:
{
? "compilerOptions": {
? ? "rootDirs": [
? ? ? "src/zh",
? ? ? "src/de",
? ? ? "src/#{locale}"
? ? ]
? }
}
編譯器現(xiàn)在可以將import messages from './#{locale}/messages'解析為import messages from './zh/messages'用做工具支持的目的,并允許在開發(fā)時不必了解區(qū)域信息颂翼。
跟蹤模塊解析
如之前討論晃洒,編譯器在解析模塊時可能訪問當前文件夾外的文件慨灭。 這會導(dǎo)致很難診斷模塊為什么沒有被解析,或解析到了錯誤的位置球及。 通過 --traceResolution啟用編譯器的模塊解析跟蹤缘挑,它會告訴我們在模塊解析過程中發(fā)生了什么。
假設(shè)我們有一個使用了typescript模塊的簡單應(yīng)用桶略。 app.ts里有一個這樣的導(dǎo)入import * as ts from "typescript"。
│? tsconfig.json
├───node_modules
│? └───typescript
│? ? ? └───lib
│? ? ? ? ? ? ? typescript.d.ts
└───src
? ? ? ? app.ts
使用--traceResolution調(diào)用編譯器诲宇。
tsc --traceResolution
輸出結(jié)果如下:
======== Resolving module 'typescript' from 'src/app.ts'. ========
Module resolution kind is not specified, using 'NodeJs'.
Loading module 'typescript' from 'node_modules' folder.
File 'src/node_modules/typescript.ts' does not exist.
File 'src/node_modules/typescript.tsx' does not exist.
File 'src/node_modules/typescript.d.ts' does not exist.
File 'src/node_modules/typescript/package.json' does not exist.
File 'node_modules/typescript.ts' does not exist.
File 'node_modules/typescript.tsx' does not exist.
File 'node_modules/typescript.d.ts' does not exist.
Found 'package.json' at 'node_modules/typescript/package.json'.
'package.json' has 'types' field './lib/typescript.d.ts' that references 'node_modules/typescript/lib/typescript.d.ts'.
File 'node_modules/typescript/lib/typescript.d.ts' exist - use it as a module resolution result.
======== Module name 'typescript' was successfully resolved to 'node_modules/typescript/lib/typescript.d.ts'. ========
需要留意的地方
導(dǎo)入的名字及位置
======== Resolving module 'typescript' from 'src/app.ts'. ========
編譯器使用的策略
Module resolution kind is not specified, using 'NodeJs'.
從npm加載types
'package.json' has 'types' field './lib/typescript.d.ts' that references 'node_modules/typescript/lib/typescript.d.ts'.
最終結(jié)果
======== Module name 'typescript' was successfully resolved to 'node_modules/typescript/lib/typescript.d.ts'. ========
使用--noResolve
正常來講編譯器會在開始編譯之前解析模塊導(dǎo)入际歼。 每當它成功地解析了對一個文件 import,這個文件被會加到一個文件列表里姑蓝,以供編譯器稍后處理鹅心。
--noResolve編譯選項告訴編譯器不要添加任何不是在命令行上傳入的文件到編譯列表。 編譯器仍然會嘗試解析模塊纺荧,但是只要沒有指定這個文件旭愧,那么它就不會被包含在內(nèi)。
比如
app.ts
import * as A from "moduleA" // OK, moduleA passed on the command-line
import * as B from "moduleB" // Error TS2307: Cannot find module 'moduleB'.
tsc app.ts moduleA.ts --noResolve
使用--noResolve編譯app.ts:
可能正確找到moduleA宙暇,因為它在命令行上指定了输枯。
找不到moduleB,因為沒有在命令行上傳遞占贫。
常見問題
為什么在exclude列表里的模塊還會被編譯器使用
tsconfig.json將文件夾轉(zhuǎn)變一個“工程” 如果不指定任何 “exclude”或“files”桃熄,文件夾里的所有文件包括tsconfig.json和所有的子目錄都會在編譯列表里。 如果你想利用 “exclude”排除某些文件型奥,甚至你想指定所有要編譯的文件列表瞳收,請使用“files”。
有些是被tsconfig.json自動加入的厢汹。 它不會涉及到上面討論的模塊解析螟深。 如果編譯器識別出一個文件是模塊導(dǎo)入目標,它就會加到編譯列表里烫葬,不管它是否被排除了界弧。
因此,要從編譯列表中排除一個文件厘灼,你需要在排除它的同時夹纫,還要排除所有對它進行import或使用了/// <reference path="..." />指令的文件。