深入 tsconfig : baseUrl 详解

461 阅读4分钟

在 TypeScript 项目中,baseUrl 主要用于模块路径解析。它为编译器提供了一个基础路径,当导入模块时,编译器会以这个baseUrl 为起点来查找模块文件。

例如,假设你的项目结构中有一个 src 目录,里面包含了各种模块文件。如果设置 baseUrlsrc,那么在导入模块时,编译器就会从src目录开始寻找对应的模块,而不是从当前文件所在的目录开始。

假设,有如下项目目录结构:

project
├── module
│   └── calc.ts
├── src
│   └── module
│       └── calc.ts
│── index.ts
└── tsconfig.json

calc.ts 的代码如下,导出 add 函数:

function add(a: number, b: number) {
  return a + b
}

export {
  add
}

index.ts 的代码如下:

import { add } from 'module/calc'

add(2, 3)

如果 baseUrl 的配置为:

{
  "compilerOptions": {
    "baseUrl": "./"
  }
}

index.ts 引入的 calc 模块为 src 文件夹外面定义的。

如果 baseUrl 的配置为:

{
  "compilerOptions": {
    "baseUrl": "./src",
  }
}

index.ts 引入的 calc 模块为 src 文件夹里面定义的。

还有,当 TypeScript 编译器在解析模块路径时,通过 baseUrl 配置进行的路径解析具有比从 node_modules 查找更高的优先级

通常情况下,在模块解析过程中,当尝试导入一个模块时,编译器会查看 node_modules 目录来寻找匹配的模块。例如,当使用 import React from 'react'; 时,编译器会首先在 node_modules 目录中查找 react 模块。

如果配置了 baseUrl,编译器会先按照 baseUrl 指定的方式来解析模块路径。例如,假设 baseUrl 被设置为 src ,并且在 TypeScript 代码中有这样的导入语句 import { myModule } from 'my-module'; ,编译器会优先尝试从 src 目录下查找 my-module 相关的文件,而不是直接去 node_modules 查找。

只有在按照 baseUrl 的配置无法找到合适的模块时,编译器才会考虑从 node_modules 进行查找。

使用 baseUrl 配置,可以简化模块导入的相对路径写法

例如,从 src/utils/file1.ts 文件导入 src/services/file2.ts 中的模块,可能需要写成 import { someFunction } from '../services/file2'; 这种相对路径形式。

当设置了 baseUrl (如设为 src)后,导入路径可以简化为 import { someFunction } from 'services/file2'; 。这样的路径更简洁直观,尤其在项目结构复杂、模块层次较多的情况下,能够减少因相对路径过长或嵌套过深而导致的错误。

baseUrl 也可以和 paths 配合使用,配置路径别名路径别名允许为特定的模块路径设置更简洁的名称。使用路径别名也是方便模块导入的一个手段,不用写那么长的模块路径,例如,将 src/components 路径设置为 @components 别名。如下面的配置:

30.png

src/components 路径设置为 @components 别名。

不过要注意的是,js 本身是不认识别名的,比如 @components 。直接运行下面的代码,是会报错的:

import { add } from '@components/calc';
const total = add(2, 3);
console.log('total  ', total);

31.png

因此,在 TypeScript 中配置了路径别名后,通常都需要借助构建工具,比如 Webpack 、Rollup 等把路径别名替换为真实的路径,比如下面的 Webpack 配置:

32.png

从 TypeScript 4.1 开始,也可独自使用 paths 配置路径别名,只是不能使用非相对路径,如下面的配置:

33.png

js 不仅不认识别名,baseUrl 配置也是不认识的,也需要使用 Webpack 等构建工具,将 baseUrl 替换为真实路径。如下面的配置,配置了 baseUrl 为 src 目录(受 include 影响):

{
  "compilerOptions": {
    "baseUrl": ".",
    "outDir": "dist",
    "target": "es6",
    "module": "es6",
    "esModuleInterop": true
  },
  "include": ["src"]
}

整个的项目结构如下:

34.png

由于配置了 baseUrl,在 index.ts 文件中可直接通过 my-util 引入 isArr 函数:

import { isArr } from 'my-util'

console.log('isArr  ', isArr([1, 2, 3]))

但是使用 tsc 编译出来的源码是无法执行的,会报 ERR_MODULE_NOT_FOUND 的错误:

35.png

因为 baseUrl 是 TypeScript 的东西,JavaScript 是不认识他的,如果要生成能够执行的代码,需要使用 Webpack 将 baseUrl 替换为真实路径:

36.png

$ 表示精确匹配,具体可见 resolve.alias

与 include 的关联

include 配置项用于指定哪些文件或文件夹应该被 TypeScript 编译器包含进来编译。

include 配置会影响到 baseUrlbaseUrl 会以 include 中的路径为基准。

如下面的配置,设置 includesrcbaseUrl. ,则实际上 baseUrl 不是项目的根目录,而是 src

37.png

整体的项目目录结构如下:

38.png

可以发现根目录下的 index.ts 是无法引入 my-util 的:

39.png

如果将 include 配置去掉,则 TypeScript 编译器会将整个项目根目录都包进来处理,baseUrl 也是指项目根目录,受 baseUrl 影响,在 src 文件夹下的 index.ts 引入的也是根目录下的 my-util 模块:

40.png

总结

baseUrl 用于配置 TypeScript 编译器查找模块的起点。

baseUrl 可用于简化模块导入的路径,也可与 paths 配合使用,配置路径别名。

要注意的是,js 是不认识 baseUrl 和路径别名的,因此为了生成可以运行的代码,需要借助 Webpack 、Rollup 等构建工具将 baseUrl 、路径别名替换为真实的路径。

include 配置会影响到 baseUrlbaseUrl 会以 include 中的路径为基准。