TypeScript模块解析

249 阅读3分钟

ts的模块解析策略ModuleResolution,路径别名BaseUrl & Paths以及虚拟目录rootDirs相关,而这几个配置项主要的作用和含义如下:

ModuleResolution

{
    "compilerOptions": {
        "moduleResolution": "node" // "node" or "classic"
    }
}

TypeScript在编译时的模块解析策略,在不显式声明的情况下,针对CommonJS模块的策略默认为"node",其他规范模块(amdsystemumdes2015esnext)为"classic"

大部分情况下你应该将其设置为"node""classic"主要用于对以前版本ts的兼容,这两种策略下的模块解析差别如下:

classic

相对路径引用

import { b } from "./moduleB"

当前文件所处目录为/root/src/folder/A.ts,在定位解析moduleB时,文件查找顺序如下:

  1. /root/src/folder/moduleB.ts
  2. /root/src/folder/moduleB.d.ts

注意:引用不解析文件夹下的index文件/root/src/folder/index.ts,即文件无法将index.ts作为文件夹下模块的公共出口,文件引用需要带具体文件名。

非相对路径导入

import { b } from "moduleB"

当前文件所处目录为/root/src/folder/A.ts,在定位解析moduleB时,文件查找顺序如下:

  1. /root/src/folder/moduleB.ts
  2. /root/src/folder/moduleB.d.ts
  3. /root/src/moduleB.ts
  4. /root/src/moduleB.d.ts
  5. /root/moduleB.ts
  6. /root/moduleB.d.ts
  7. /moduleB.ts
  8. /moduleB.d.ts

node

node参照的是Node中CommonJS的模块解析规范。 相对路径引用,不存在则将引用的地址视为文件夹,定位文件夹下是否有package.json(如果存在main字段,则定位到main指向的模块地址)

相对路径引用

import { b } from "./moduleB"

CommonJS的解析顺序如下:

  1. 先查找文件下命名为moduleB.js的文件是否存在;
  2. moduleB视为文件夹,查找文件夹下是否存在package.json及声明main属性,符合条件则将模块解析定位为该属性键值对应的地址,如果存在{ "main": "lib/mainModule.js" },那么解析后模块定位为moduleB/lib/mainModule.js
  3. 读取moduleB/index.js

TypeScript的解析和上面基本一致,只是额外支持了.tsx.d.ts的解析,此外读取package.json的属性为types

  1. /root/src/moduleB.ts
  2. /root/src/moduleB.tsx
  3. /root/src/moduleB.d.ts
  4. /root/src/moduleB/package.json ("types")
  5. /root/src/moduleB/index.ts
  6. /root/src/moduleB/index.tsx
  7. /root/src/moduleB/index.d.ts

非相对路径引用

import { b } from "moduleB"

仍假定当前文件所处目录为/root/src/folder/A.ts,CommonJS的解析顺序如下:

  1. /root/src/node_modules/moduleB.js
  2. /root/src/node_modules/moduleB/package.json ("main")
  3. /root/src/node_modules/moduleB/index.js

本层找不到会跳到上级目录下的node_modules进行查找:

  1. /root/node_modules/moduleB.js
  2. /root/node_modules/moduleB/package.json ("main")
  3. /root/node_modules/moduleB/index.js

TypeScript和这上面基本类似,具体如下:

  1. /root/src/moduleB.ts
  2. /root/src/moduleB.tsx
  3. /root/src/moduleB.d.ts
  4. /root/src/moduleB/package.json (if it specifies a types property)
  5. /root/src/moduleB/index.ts
  6. /root/src/moduleB/index.tsx
  7. /root/src/moduleB/index.d.ts

也会在本层目录找不到时,去到上层的node_modules中按以上顺序查找,直至根目录。

BaseUrl & Paths

{
    "compilerOptions": {
    "baseUrl": ".", // 作为path映射的rootPath
    "paths": {
            "*": ["*", "generated/*"] // 相对于baseUrl的path
        }
    }
}

baseUrlpaths需要配合使用,类似于webpack中的resolve.alias用于设置别名路径。

以上*代表匹配任意路径名,<moduleName> => <baseUrl>/<moduleName>,匹配不上则继续匹配<moduleName> => <baseUrl>/generated/<moduleName>

RootDirs

{
    "compilerOptions": {
        "rootDirs": ["src/views", "generated/templates/views"]
    }
}

rootDirs用于运行时将数组内的目录合为同一个虚拟目录。

假设当前目录如下:

src 
└── views 
    └── view1.ts (imports './template1') 
    └── view2.ts 
generated 
    └── templates 
        └── views 
        └── template1.ts (imports './view2')
        

view1.ts内的引入会在查找srv/view目录下且未发现目标模块后,继续查找generated/templates/views以期望找到template1模块。

reference

TypeScript Module Resolution