0%

typescript-config-outdir

今天在玩耍typescript的时候,发现了一个有趣的问题,tsconfig.json中的outDir配置导致tsc命令无法编译,tsconfig.json的配置是这样的:

1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "src/",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

再来看一下项目的目录结构(这里推荐一个在线生成项目目录结构的工具,是我自己写的,非常好用,如果大家有需要,不妨一试:https://ascii-tree.com/)

1
2
3
4
my-app
├─ src
│ └─ add.ts
└─ tsconfig.json

当我在terminal中使用tsc命令编译项目时,顺利生成了add.js文件, 于是我想,不如加上outDir吧,于是我就加上了。

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "src/",
"outDir": ".",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

当我再次运行tsc命令时,发现编译失败了,报错信息如下:

1
No inputs were found in config file 'xxx/tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude' paths were '["xxx"]'.

于是我把outDir删除了,再次编译,又能成功编译了,太邪门了!索性去官网查了一下outDir的详细说明: https://www.typescriptlang.org/tsconfig/#outDir

1
2
3
If specified, .js (as well as .d.ts, .js.map, etc.) files will be emitted into this directory. The directory structure of the original source files is preserved; see rootDir if the computed root is not what you intended.

If not specified, .js files will be emitted in the same directory as the .ts files they were generated from:

这段话的大意是说:如果指定了outDir,那么编译后的.js文件会被输出到指定的目录中,并且会保留原始源文件的目录结构;如果没有指定outDir,那么.js文件会被输出到和.ts文件相同的目录中。- 仔细看了两遍,没看出什么有用的信息。

后来问了一下Copilot,它说:outDir指定的目录会自动加入exclude中,也就是tsc不会编译该目录下的文件。这就说得通了,因为我指定了outDir./rootDirsrc/,而src目录是根目录的子目录,既然根目录被排除掉了,那么src目录下的文件自然也会被排除掉,这相当于所有src目录下的文件都不会参与编译!

所以我们最好将outDir指定为一个和rootDir没有任何交集的目录,通常我们指定为dist/,也就是编译后的文件放在dist/目录下,这样就不会和源文件有交集了。比如:

outDir改成了dist, 再次编译,大功告成!

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "src/",
"outDir": "dist/",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

那么有人说了,如果我非要指定outDir./呢?难道就没有办法编译了吗?当然不是,如果非要指定outDir./,可以通过files来指定需要编译的文件,因为files指定的文件是不受exclude选项影响的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "src/",
"outDir": "./",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"files": [
"./src/add.ts"
]
}

这样就可以编译了,当然你也可以指定多个文件,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "src/",
"outDir": "./",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"files": [
"src/add.ts",
"src/index.ts"
]
}

可是这样也有问题,随着项目的增大,文件会越来越多,而files又不支持通配符,这导致我们需要将所有需要编译的文件都写到files中,实在是太麻烦了。于是我们考虑用include来指定需要编译的文件,比如将exclude设置为[],表示没有文件会被exclude。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "src/",
"outDir": "./",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": [
"src/**/*"
]
}

但是运行tsc时你会发现,还是报错:error TS18003: No inputs were found in config file, 为啥呢?因为outDir指定的目录会自动加入exclude中,outDir指定的目录是./,也就是项目的根目录,而src目录是根目录的子目录,所以会被排除掉,导致报错。

我们可以通过显示指定exclude来解决这个问题,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "src/",
"outDir": "./",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": [
"src/**/*"
],
"exclude": []
}

结论

  1. 不指定outDir,tsc会在每个ts文件所在的目录生成对应的js文件。
  2. 指定outDir,tsc会在指定的目录生成对应的js文件。比如指定outDirdist/,则会在dist/目录下生成对应的js文件。
  3. outDir指定的目录会自动加入exclude中,也就是tsc不会编译该目录下的文件。
  4. outDir指定的目录必须和rootDir不同,而且不能有交集,也就是不能有包含和被包含关系,否则会报错。
  5. outDirrootDir有交集的时候,需要通过files来指定需要编译的文件。注意不能使用include,因为exclude会排除掉outDir目录下的文件。但是files不受exclude影响。