本文来梳理一下我个人会用到的或者我认为有用的一些tsconfig选项,根据我的使用情况,本文会保持更新,目前最新的TypeScript版本是5.5.4
。
主要的参考资料是官网TSConfig Reference,还会参考schemastore的tsconfig.json文件。尽管后者会更全面一些,但是通常情况下没有记录在文档中的字段是不稳定或不常用的,所以我会以前者为主,后者为辅。
tsconfig.json
文件中顶级的字段一共有8个,分别是 compilerOptions
files
extends
include
exclude
references
watchOptions
typeAcquisition
。compilerOptions
是最主要的字段,包含编译的相关选项;files
include
exclude
三个字段是和配置文件的作用范围相关的;extends
用于复用配置文件;references
用于优化项目的结构,提升编译效率;watchOptions
用于配置watch功能;typeAcquisition
用于配置纯js项目中用到的第三方库类型文件。其中,最后两个字段watchOptions
和typeAcquisition
本文不涉及,因为我通常针对整个项目使用tsc --watch
,也不习惯在纯js项目中配置jsconfig.json
,所以暂时就不研究了,如果后续我实际用到了再补充。
TSConfig Reference
作用范围
一个项目有多份配置文件是非常正常的,比如一个Vue项目,前端页面一份配置,其运行环境是浏览器;打包工具一份配置,其运行环境是Node.js。如果再有单元测试、端到端测试等等那就还会有更多的配置文件。这种情况下,files
exclude
include
这三个字段可以指定每份配置文件作用于哪个部分。
files
,其值是一个具体的文件名组成的数组,不允许使用通配符。比如专门给Vite写的配置文件,可以用"files": ["vite.config.ts"]
来指明这份配置文件只作用于vite.config.ts这一个文件,其他文件管不着。include
,和files
选项类似,值也是数组,不过同时支持通配符。比如"include": ["src/**/*"]
就代表src文件夹及其子文件夹下的所有相关文件。个人认为include
完全可以替代files
,用这个字段就够了。exclude
,用于排除include
中的内容,同样支持通配符。比如,前面include把src目录下所有文件都包含进去了,但是单元测试目录下文件的配置规则和正常文件不一样,可以使用"exclude": ["src/**/__test__/*"]
排除对应的文件。
配置复用
extends
字段用于继承配置文件,就相当于把父文件中的所有配置复制粘贴到当前文件。一个项目有多个配置文件的情况下,可以创建一个tsconfig.common.json
来配置项目级别的通用选项,这样就不必在每个配置文件中都写一遍了。
需要说明的是,继承的配置文件也可以来自第三方包。这里推荐两个项目,社区的tsconfig/bases项目包含了很多常用场景下的配置;以及Vue官方的tsconfig项目包含了Vue开发的推荐配置。
结构优化
references
字段用于优化项目的编译效率,一般情况下只有大型的项目才有必要配置,小型项目即使配置了也没有太多优化空间。官方有一个指南专门用于介绍这个功能如何使用。我认为官方的指南已经非常不错了,这里就不重复啰嗦了,只推荐一个Real World的示例,就是TypeScript项目自身的tsconfig.json,可以搭配学习。
项目编译
compilerOptions
字段下的选项非常多,官方按功能分为了以下14个部分:
|
|
其中 5. Editor Support
7. Backwards Compatibility
9. Compiler Diagnostics
11. Output Formatting
14. Watch Options
几个部分用处不大,本文不涉及。
Type Checking
这部分的选项主要用于类型检查,完全不设置也是没有问题的,并不会影响代码的产物。虽然看着一大堆选项,但大部分的默认值是非常合理的,不需要额外设置。下面推荐几个需要配置的选项:
- exactOptionalPropertyTypes,推荐设置为
true
,针对的是带?
的可选的属性,打开之后允许不设置该属性的值,但不允许手动将其值设置为undefined。 - noFallthroughCasesInSwitch,推荐设置为
true
,打开之后switch语句的case分支必须写break/return/throw,这是非常必要的。 - noUnusedLocals,看个人习惯,设置为
true
可以避免声明了变量却不使用的情况,但是由于现在打包都支持tree shaking,产物中这些无用的代码都会被移除,所以个人认为设置与否问题不大。 - noUnusedParameters,类似上一条,这个针对的是函数声明中没有使用到的参数,我的习惯是都设置为true。
- strict,强烈推荐设置为
true
,会帮助检查一些常用的类型错误,比如某个参数没有指定类型,某个变量标注了string类型却没有初始化(这意味着它实际上是undefined)等,这些都是非常有用的检查
Modules
不同于类型检查,这部分的配置影响的是文件引用的模块解析规则,如果错误配置,会导致项目编译失败。下面是推荐配置的一些选项:
- allowImportingTsExtensions,这个选项在项目使用了外部解析器时可以打开,比如使用了vite、bun等工具,因为这些工具会帮助修改最终的输出文件,只使用tsc的情况下,打开此选项会导致编译产物无法运行。
- module和moduleResolution,前者告诉编译器项目使用的是什么模块,后者告诉编译器如何解析模块。一个现代的项目,如果是Web项目,那么推荐设置
module
为es2022
或esnext
,对应的moduleResolution
设置为bundler
,这将告诉编译器此项目使用了ESModule,在解析时相对路径时可以不指定文件扩展名(使用ESModule的import导入相对路径的文件时是要求有扩展名的,但是在实际项目中打包器如webpack、vite会帮我们找到合适的文件);如果是Node.js等非后端项目,那么推荐将module
设置为node16
或nodenext
,对应的moduleResolution
也是node16
或nodenext
,这告诉编译器项目使用ESModule,并且严格按照ESModule解析。 - moduleSuffixes,相对路径不使用扩展名的情况下,告诉编译器解析这个路径时需要寻找哪些后缀的文件,通常对于React Native等涉及到移动端的项目有用。
- paths,用于设置别名,其值是一个json对象,对象中的key出现在import语句中,比如
import something from "@/utils"
,如果不指定paths
,编译器将不知道从哪里找utils文件,因为并没有一个叫@
的文件夹,这种情况下,设置"paths": { "@/*": ["./src/*"] }
,可以告诉编译器@/utils
其实就是./src/utils
,这样就可以找到对应的文件了。 - resolveJsonModule,设置为
true
之后就可以直接用import语句引入json文件了,根据项目需要设置 - rootDir,影响输出文件的目录结构,默认是配置文件作用范围内的最长的一段公共路径。例如,配置文件所在的项目根目录下有一个
index.ts
文件,那么默认的值就是"."
,即配置文件所在的目录,注意.d.ts
的这种声明文件会被忽略,通常这个值不需要额外设置,但是有必要知道它。 - types,用于指定包含哪些包的类型文件,默认值是所有
@types/*
类型包,通常在项目的次要配置文件中配置。比如只针对测试的tsconfig.test.json
中,可以设置"types": ["node", "jsdom"]
忽略除此之外的类型包。
没有涉及到的选项,我个人认为不需要设置,一般也用不到。
Emit
这个部分的选项控制的是编译产物的输出。由于现在很多项目会使用打包工具,不会使用tsc
来编译获取输出产物,所以大多数情况下,只需要配置一个"noEmit": true
即可。
使用tsc
工具的情况下,推荐设置的选项如下:
- declaration,为ts文件生成
.d.ts
声明文件。通常对于定位是Library的项目有必要开启,如果是Application就没必要了。 - inlineSourceMap、inlineSources和sourceMap,都是控制source map(源码映射)相关的选项,inlineSourceMap控制是否生成文件内的源码映射,sourceMap对应的是将映射写为单独的
.js.map
文件,inlineSources则控制是否在源码映射中包括源码本身。开发环境建议开启sourceMap
和inlineSources
,生产环境建议全部关闭,因为这会导致源码泄露。如果生产环境要开启,方便内部人员排查问题,那么建议配置sourceRoot
和mapRoot
字段,这种配置比较高阶,这里就不展开了。 - removeComments,是否移除代码中的注释,推荐生产环境设置为true,减小产物的体积。
- stripInternal,忽略JSDoc注释中带有
@internal
字样的代码,这对于一些Library项目很有用,避免包括内部的API给库的使用人员。一个现实的示例是CodeMirror项目的tsconfig.json。
JavaScript Support
这个部分主要用来告诉编译器ts项目中的js文件如何处理。理想情况下,ts项目就不应该出现js文件,互相掺杂还不如不用ts,但是现实是,有一些第三方库用js写的,甚至没有@types
类型包,或者,有一些功能用ts写纯粹就是找罪受,但是这些也属于整体项目的一部分。因此合理处理ts项目中的js文件是很有必要的。
建议将allowJs设置为true,允许js文件存在即可,其余选项可以不用管。
Interop Constraints
推荐手动配置下面几个选项:
- verbatimModuleSyntax,建议设置为true。这样就可以使用
import type { A } from "a";
语法来导入类型,而这些类型会在编译的产物中被抹除。如果不开启,导入类型和导入变量看上去没有区别,某些情况下会出现编译产物和预期不符的问题。 - allowSyntheticDefaultImports,建议设置为true。允许使用
import React from "react";
来替代原本的import * as React from "react";
语法。module的值设置为system或moduleResolution设置为bundler就会自动打开此选项。 - esModuleInterop,建议设置为true。开启之后会修复TypeScript转译代码的两个问题,更严格按照ESModule规范来编译,module设置为node16和nodenext会自动开启。同时此选项开启之后也会自动开启allowSyntheticDefaultImports选项。
Language and Environment
推荐设置如下几个选项:
- emitDecoratorMetadata/experimentalDecorators,这两个选项判断是否需要开启很简单,代码使用装饰器就开启,否则保持默认关闭的状态。可以阅读官方手册以及Mirone的TypeScript装饰器完全指南来了解装饰器。另外,Angular和Nest两个项目都大量使用了装饰器。
- jsx/jsxFactory/jsxFragmentFactory/jsxImportSource,根据使用的框架来配置这四个选项,如果使用React,推荐配置
"jsx": "react-jsx"
;如果是Vue,推荐配置"jsx": "preserve"
以及"jsxImportSource": "vue"
。推荐阅读Bun文档中关于jsx的配置说明 - lib/target,lib用来指定TypeScript内置的JS API,即在源代码中可以使用的API,target用于指定编译产物的运行环境,即编译后的产物可以使用的API。前者一般我会配置为esnext,这允许我在源代码中使用现代的API,更顺手;后者我习惯配置为es2020,这支持了大多数现代浏览器,一定程度避免了JS新API的兼容问题。
- useDefineForClassFields,推荐设置为true。由于TypeScript很早就引入了
class
字段,所以和目前的标准会有些不同,设置为true会按照最新标准来。这对于开发者是无感的,因为语法没有任何变化,只是runtime实现有区别。
Projects
这个部分推荐将composite设置为true,其他选项保持默认即可。关于此选项的解释可以参考官方手册
Completeness
这个部分只有一个字段,skipLibCheck,设置为true可以跳过类型检查节省编译的时间。我个人对于这个选项的见解有限,不过Vue.js官方的推荐配置以及Bun的推荐配置还有tsconfig社区的推荐配置都是设置为true,所以通常我也会设置为true。
Command Line
TypeScript
官方的命令行工具有两个,分别是tsc
和tsserver
,前者是一个编译器,可以通过tsc -h
查看帮助内容;后者是封装了编译器和语言服务的服务器,主要提供给IDE使用,提供类型补全和检查等能力。
总结
对于tsconfig的配置,我个人偏好严格一些,本文的推荐设置也是偏严格的,因为我选择使用TypeScript而不是JavaScript就是为了能够在代码运行之前发现一些问题。但是现实中每个人或团队的偏好不同,每个项目的需求也不同,最终还是要根据实际情况来修改配置。下面是根据上面的描述配置的一份适用于Web应用的tsconfig.json
示例,
|
|