广告:Codex Token 低价中转站稳定接口 · 快速接入 · 开发者备用通道
Engineering article

我在大厂用TS编译配置:学习路线 | 看完就懂原理

我在大厂用TS编译配置踩过不少坑,最值钱的经验是:别把tsconfig.json当模板,它影响编译速度和构建结果。配置得精细,才能避免构建卡死、类型报错、打包体积膨胀等问题。比如我见过有人直接把“resolveJsonModule”设为true,结果构建时没处理好路径导致模块找不到,还得手动加“esModuleInterop”和“modu

我在大厂用TS编译配置:学习路线 | 看完就懂原理
配图来源于网络和AI生成,仅供参考。
▌ 技术引导
我在大厂用TS编译配置踩过不少坑,最值钱的经验是:别把tsconfig.json当模板,它影响编译速度和构建结果。配置得精细,才能避免构建卡死、类型报错、打包体积膨胀等问题。比如我见过有人直接把“resolveJsonModule”设为true,结果构建时没处理好路径导致模块找不到,还得手动加“esModuleInterop”和“moduleResolution”参数。如果想让TypeScript编译更轻量,一定要把“include”和“exclude”搞清楚,别让编译器把整个项目都扫描一遍。还有就是“target”设置别乱来,选ES2020或ESNext,别用ES5,否则后期打到浏览器会出问题。另外,别忘了配合Babel做兼容性处理,否则你写的函数式组件会在旧版本浏览器里死掉。

我见过很多项目把“outDir”和“rootDir”搞反,导致打包出来的文件结构混乱。在实践中,我习惯把“outDir”放在“dist”下面,而“rootDir”指向“src”。这样能保证输出目录结构清晰,不至于被第三方工具搞懵。还有就是“strict”模式,别一开始就全开,得根据项目情况逐步启用,否则编译器直接卡在类型检查里,效率极低。而且,有些项目用esbuild替代了tsc,但配置没对齐,导致代码无法正确打包。

我曾用“declaration”配合“declarationDir”生成d.ts文件,结果发现有些模块没声明,还得手动加“types”配置。另外,对node_modules的处理也容易出问题,尤其是在使用monorepo结构时,得用“typeRoots”或“types”指定正确的类型路径。有些项目甚至用“typeRoots”把全局类型包全扫进来,反而让编译变慢。还有就是“jsx”配置,千万别随便写“react”,得配合“jsxFactory”和“jsxImportSource”一起用,否则你写的JSX代码会出错。

在实际部署中,我通常用“build”脚本配合“tsc --build”执行多配置,这样能根据不同环境生成不同版本的代码。比如开发环境用“--watch”和“--noEmit”加快响应,生产环境用“--build”全量编译并打包。一些同学喜欢用“--module”设为“ESNext”,但得注意它不兼容旧版Node.js版本,最好用“CommonJS”或“UMD”模式。还有就是“lib”配置,别乱加,除非你确实需要用到ES2024新特性,否则会增加编译负担。

总之,TS编译配置不是一成不变的,得根据项目结构、构建工具、部署目标动态调整。我见过有人把“tsconfig.json”写得像配置文件,结果编译器根本不认,得用“--config”参数指定路径。或者有人在mixins里用了“import”语法,但没配置“module”或“moduleResolution”,导致模块解析失败。这些细节都得踩过才能明白,别指望看文档就能搞明白,得在真实项目里反复试错,才能找到合适的配置。

▌ 技术参考

TS编译配置最核心的是tsconfig.json,它定义了编译器的行为。在大厂项目中,通常会把编译参数拆分成多个文件,比如使用“extends”字段继承基础配置。基础配置可能包含“target”、“module”、“moduleResolution”、“jsx”等字段,而具体项目再覆盖“include”、“exclude”、“outDir”等。例如:
```json
{
"extends": "./base/tsconfig.base.json",
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"jsx": "react"
}
}
```
这种结构有助于团队协作和配置复用。我曾在一个项目里因为没继承基础配置,导致所有模块都编译了,结果build速度慢到不可接受。


编译器参数需要和构建工具配合使用。例如,使用Vite时,tsconfig.json里的“target”和“module”要和Vite的配置对齐,否则会触发不必要的转换。在Vite中,通常用“--config”参数指定tsconfig路径,同时通过“build.transpileOnly”控制是否进行类型检查。比如:
```bash
vite build --config tsconfig.prod.json
```
如果没设置好“outDir”,Vite会把编译后的文件打到根目录,这会破坏项目结构。我之前一个项目因为outDir没设置,导致dist文件夹里有多个子目录,打包后路径错误。


“strict”模式是常见的坑点。它包含了“strictNullChecks”、“strictFunctionTypes”等子选项,如果全部开启,就会让很多开发者感到不适。尤其是老项目,代码写法不规范,开“strict”会报一堆问题。因此,我建议在逐步升级项目时,先启用“strictNullChecks”和“strictFunctionTypes”,再慢慢添加其他选项。比如:
```json
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
```
这样既保证了类型安全,又不会让编译器卡死。有时候,因为没有开启“strict”,导致类型错误没被发现,后期修改代码才发现问题。


“jsx”配置必须和JSX语法配合使用,否则会出问题。一般开发环境用“react”模式,生产环境用“react-jsx”模式,这样可以减少不必要的处理。例如:
```json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
"jsxImportSource": "preact"
}
}
```
有些项目为了兼容,甚至同时启用“react”和“react-jsx”,结果代码在某些环境下无法正常运行。我曾在一个React项目里因为没配置“jsxImportSource”,导致导入React组件失败。


“target”设置要根据项目需求来定。如果项目用的是现代浏览器,建议设为“ES2020”或“ESNext”,但如果部署环境是旧版Node.js,最好用“ES2015”或“ES6”。例如:
```json
{
"compilerOptions": {
"target": "ES2020"
}
}
```
但如果你用的是Node.js 14,这个配置会导致编译器抛出错误。我曾遇到一个项目,因为target设为ES2020,而Node.js版本过低,最终build失败。


“outDir”和“rootDir”配置容易被忽视,但它们直接影响输出结构。如果没设置,编译器会把所有文件打到当前目录,导致混乱。我习惯把“outDir”设为“dist”,而“rootDir”设为“src”,这样可以确保输出文件不会和源文件混在一起。例如:
```json
{
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
}
}
```
有些项目为了简化,直接把“outDir”设为“.”,结果build后代码结构完全乱掉。


“include”和“exclude”配置不能随便写,否则会编译太多或太少文件。例如,如果项目结构是“src”、“utils”、“types”等目录,那么“include”应该明确指定需要编译的文件。我曾在一个项目里把“include”设为“src//”,结果编译器把所有文件都扫了一遍,包括测试文件和文档,build时间暴涨。


“typeRoots”和“types”配置在monorepo项目中非常关键。如果多个子项目共用类型,但没配置好typeRoots,会导致类型文件加载失败。例如:
```json
{
"compilerOptions": {
"typeRoots": ["./types", "./node_modules/@types"],
"types": ["react", "react-dom"]
}
}
```
有些项目会把所有类型都加到“types”里,结果导致编译器加载了太多包,反而影响性能。


“declaration”和“declarationDir”是生成d.ts文件的必选项。如果项目需要类型声明文件,必须开启这两个配置。例如:
```json
{
"compilerOptions": {
"declaration": true,
"declarationDir": "dist/types"
}
}
```
但有些项目因为没配置好,导致d.ts文件打到错误目录,或者没有生成。我曾经在某个项目里,因为没指定“declarationDir”,导致所有类型都打到“dist”目录,最终项目混乱不堪。


“importHelpers”配置可以显著提升编译速度,尤其在大型项目中。它允许TypeScript使用Babel的helper函数,而不是每次都生成新的。例如:
```json
{
"compilerOptions": {
"importHelpers": true
}
}
```
但要注意,如果项目没有使用Babel,这个配置可能无效。我曾在一个项目里开了“importHelpers”,却发现没有效果,后来才发现是因为没引入Babel的helper模块。

十一
“skipLibCheck”是避免类型错误的常用配置。它会跳过对第三方库的类型检查,这样能减少编译时间。例如:
```json
{
"compilerOptions": {
"skipLibCheck": true
}
}
```
不过,如果项目依赖的第三方库有类型错误,这个配置会隐藏问题。我曾在一个项目里开了“skipLibCheck”,结果运行时发现很多第三方库的类型定义不明确,最后还得手动处理。

十二
“resolveJsonModule”配置要和“esModuleInterop”一起用,否则会加载错误模块。比如:
```json
{
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true
}
}
```
如果只开“resolveJsonModule”,编译器可能会把JSON文件当作模块导入,导致找不到模块的错误。我曾在一个项目里没有设置“esModuleInterop”,结果导入JSON文件时报错。

十三
“moduleResolution”配置决定了如何解析模块路径。常见的有“node”和“classic”两种模式。如果项目使用了ES模块,建议用“node”模式,因为它支持相对路径和node_modules。例如:
```json
{
"compilerOptions": {
"moduleResolution": "node"
}
}
```
有些项目用了“classic”模式,但又用ES模块,导致模块解析失败。我曾在一个项目里因为“moduleResolution”设为“classic”,而用到了ES模块语法,编译器直接报错。

十四
“lib”配置决定了编译器使用的JavaScript库。如果项目需要用到ES2024的新特性,必须添加对应的库。例如:
```json
{
"compilerOptions": {
"lib": ["ES2024", "DOM"]
}
}
```
但有些项目因为没加“DOM”,导致浏览器找不到某些API,比如“fetch”或“Promise”。我曾在一个前端项目里没有配置“DOM”,结果代码在浏览器运行时报错。

十五
“noEmit”配置在开发环境非常有用,它可以防止编译器生成代码。比如:
```json
{
"compilerOptions": {
"noEmit": true
}
}
```
这样可以避免每次保存都生成文件,同时保留类型检查。在生产环境,再通过“--build”参数单独触发生成。我曾在一个项目里没用“noEmit”,导致每次保存都要编译,build速度极慢。