本文来梳理一下我个人会用到的或者我认为有用的一些tsconfig选项,根据我的使用情况,本文会保持更新,目前最新的TypeScript版本是5.5.4

主要的参考资料是官网TSConfig Reference,还会参考schemastoretsconfig.json文件。尽管后者会更全面一些,但是通常情况下没有记录在文档中的字段是不稳定或不常用的,所以我会以前者为主,后者为辅。

tsconfig.json文件中顶级的字段一共有8个,分别是 compilerOptions files extends include exclude references watchOptions typeAcquisitioncompilerOptions是最主要的字段,包含编译的相关选项;files include exclude三个字段是和配置文件的作用范围相关的;extends用于复用配置文件;references用于优化项目的结构,提升编译效率;watchOptions用于配置watch功能;typeAcquisition用于配置纯js项目中用到的第三方库类型文件。其中,最后两个字段watchOptionstypeAcquisition本文不涉及,因为我通常针对整个项目使用tsc --watch,也不习惯在纯js项目中配置jsconfig.json,所以暂时就不研究了,如果后续我实际用到了再补充。

TSConfig Reference

作用范围

一个项目有多份配置文件是非常正常的,比如一个Vue项目,前端页面一份配置,其运行环境是浏览器;打包工具一份配置,其运行环境是Node.js。如果再有单元测试、端到端测试等等那就还会有更多的配置文件。这种情况下,files exclude include 这三个字段可以指定每份配置文件作用于哪个部分。

  1. files ,其值是一个具体的文件名组成的数组,不允许使用通配符。比如专门给Vite写的配置文件,可以用"files": ["vite.config.ts"]来指明这份配置文件只作用于vite.config.ts这一个文件,其他文件管不着。
  2. include,和files选项类似,值也是数组,不过同时支持通配符。比如"include": ["src/**/*"]就代表src文件夹及其子文件夹下的所有相关文件。个人认为include完全可以替代files,用这个字段就够了。
  3. exclude,用于排除include中的内容,同样支持通配符。比如,前面include把src目录下所有文件都包含进去了,但是单元测试目录下文件的配置规则和正常文件不一样,可以使用"exclude": ["src/**/__test__/*"]排除对应的文件。

配置复用

extends字段用于继承配置文件,就相当于把父文件中的所有配置复制粘贴到当前文件。一个项目有多个配置文件的情况下,可以创建一个tsconfig.common.json来配置项目级别的通用选项,这样就不必在每个配置文件中都写一遍了。

需要说明的是,继承的配置文件也可以来自第三方包。这里推荐两个项目,社区的tsconfig/bases项目包含了很多常用场景下的配置;以及Vue官方的tsconfig项目包含了Vue开发的推荐配置。

结构优化

references字段用于优化项目的编译效率,一般情况下只有大型的项目才有必要配置,小型项目即使配置了也没有太多优化空间。官方有一个指南专门用于介绍这个功能如何使用。我认为官方的指南已经非常不错了,这里就不重复啰嗦了,只推荐一个Real World的示例,就是TypeScript项目自身的tsconfig.json,可以搭配学习。

项目编译

compilerOptions字段下的选项非常多,官方按功能分为了以下14个部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Type Checking
Modules
Emit
JavaScript Support
Editor Support
Interop Constraints
Backwards Compatibility
Language and Environment
Compiler Diagnostics
Projects
Output Formatting
Completeness
Command Line
Watch Options

其中 5. Editor Support 7. Backwards Compatibility 9. Compiler Diagnostics 11. Output Formatting 14. Watch Options几个部分用处不大,本文不涉及。

Type Checking

这部分的选项主要用于类型检查,完全不设置也是没有问题的,并不会影响代码的产物。虽然看着一大堆选项,但大部分的默认值是非常合理的,不需要额外设置。下面推荐几个需要配置的选项:

  1. exactOptionalPropertyTypes,推荐设置为true,针对的是带?的可选的属性,打开之后允许不设置该属性的值,但不允许手动将其值设置为undefined。
  2. noFallthroughCasesInSwitch,推荐设置为true,打开之后switch语句的case分支必须写break/return/throw,这是非常必要的。
  3. noUnusedLocals,看个人习惯,设置为true可以避免声明了变量却不使用的情况,但是由于现在打包都支持tree shaking,产物中这些无用的代码都会被移除,所以个人认为设置与否问题不大。
  4. noUnusedParameters,类似上一条,这个针对的是函数声明中没有使用到的参数,我的习惯是都设置为true。
  5. strict,强烈推荐设置为true,会帮助检查一些常用的类型错误,比如某个参数没有指定类型,某个变量标注了string类型却没有初始化(这意味着它实际上是undefined)等,这些都是非常有用的检查

Modules

不同于类型检查,这部分的配置影响的是文件引用的模块解析规则,如果错误配置,会导致项目编译失败。下面是推荐配置的一些选项:

  1. allowImportingTsExtensions,这个选项在项目使用了外部解析器时可以打开,比如使用了vite、bun等工具,因为这些工具会帮助修改最终的输出文件,只使用tsc的情况下,打开此选项会导致编译产物无法运行。
  2. module和moduleResolution,前者告诉编译器项目使用的是什么模块,后者告诉编译器如何解析模块。一个现代的项目,如果是Web项目,那么推荐设置modulees2022esnext,对应的moduleResolution设置为bundler,这将告诉编译器此项目使用了ESModule,在解析时相对路径时可以不指定文件扩展名(使用ESModule的import导入相对路径的文件时是要求有扩展名的,但是在实际项目中打包器如webpack、vite会帮我们找到合适的文件);如果是Node.js等非后端项目,那么推荐将module设置为node16nodenext,对应的moduleResolution也是node16nodenext,这告诉编译器项目使用ESModule,并且严格按照ESModule解析。
  3. moduleSuffixes,相对路径不使用扩展名的情况下,告诉编译器解析这个路径时需要寻找哪些后缀的文件,通常对于React Native等涉及到移动端的项目有用。
  4. paths,用于设置别名,其值是一个json对象,对象中的key出现在import语句中,比如import something from "@/utils",如果不指定paths,编译器将不知道从哪里找utils文件,因为并没有一个叫@的文件夹,这种情况下,设置"paths": { "@/*": ["./src/*"] },可以告诉编译器@/utils其实就是./src/utils,这样就可以找到对应的文件了。
  5. resolveJsonModule,设置为true之后就可以直接用import语句引入json文件了,根据项目需要设置
  6. rootDir,影响输出文件的目录结构,默认是配置文件作用范围内的最长的一段公共路径。例如,配置文件所在的项目根目录下有一个index.ts文件,那么默认的值就是".",即配置文件所在的目录,注意.d.ts的这种声明文件会被忽略,通常这个值不需要额外设置,但是有必要知道它。
  7. types,用于指定包含哪些包的类型文件,默认值是所有@types/*类型包,通常在项目的次要配置文件中配置。比如只针对测试的tsconfig.test.json中,可以设置"types": ["node", "jsdom"]忽略除此之外的类型包。

没有涉及到的选项,我个人认为不需要设置,一般也用不到。

Emit

这个部分的选项控制的是编译产物的输出。由于现在很多项目会使用打包工具,不会使用tsc来编译获取输出产物,所以大多数情况下,只需要配置一个"noEmit": true即可。

使用tsc工具的情况下,推荐设置的选项如下:

  1. declaration,为ts文件生成.d.ts声明文件。通常对于定位是Library的项目有必要开启,如果是Application就没必要了。
  2. inlineSourceMap、inlineSources和sourceMap,都是控制source map(源码映射)相关的选项,inlineSourceMap控制是否生成文件内的源码映射,sourceMap对应的是将映射写为单独的.js.map文件,inlineSources则控制是否在源码映射中包括源码本身。开发环境建议开启sourceMapinlineSources,生产环境建议全部关闭,因为这会导致源码泄露。如果生产环境要开启,方便内部人员排查问题,那么建议配置sourceRootmapRoot字段,这种配置比较高阶,这里就不展开了。
  3. removeComments,是否移除代码中的注释,推荐生产环境设置为true,减小产物的体积。
  4. 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

推荐手动配置下面几个选项:

  1. verbatimModuleSyntax,建议设置为true。这样就可以使用import type { A } from "a";语法来导入类型,而这些类型会在编译的产物中被抹除。如果不开启,导入类型和导入变量看上去没有区别,某些情况下会出现编译产物和预期不符的问题。
  2. allowSyntheticDefaultImports,建议设置为true。允许使用import React from "react";来替代原本的import * as React from "react";语法。module的值设置为system或moduleResolution设置为bundler就会自动打开此选项。
  3. esModuleInterop,建议设置为true。开启之后会修复TypeScript转译代码的两个问题,更严格按照ESModule规范来编译,module设置为node16和nodenext会自动开启。同时此选项开启之后也会自动开启allowSyntheticDefaultImports选项。

Language and Environment

推荐设置如下几个选项:

  1. emitDecoratorMetadata/experimentalDecorators,这两个选项判断是否需要开启很简单,代码使用装饰器就开启,否则保持默认关闭的状态。可以阅读官方手册以及Mirone的TypeScript装饰器完全指南来了解装饰器。另外,AngularNest两个项目都大量使用了装饰器。
  2. jsx/jsxFactory/jsxFragmentFactory/jsxImportSource,根据使用的框架来配置这四个选项,如果使用React,推荐配置"jsx": "react-jsx";如果是Vue,推荐配置"jsx": "preserve"以及"jsxImportSource": "vue"。推荐阅读Bun文档中关于jsx的配置说明
  3. lib/target,lib用来指定TypeScript内置的JS API,即在源代码中可以使用的API,target用于指定编译产物的运行环境,即编译后的产物可以使用的API。前者一般我会配置为esnext,这允许我在源代码中使用现代的API,更顺手;后者我习惯配置为es2020,这支持了大多数现代浏览器,一定程度避免了JS新API的兼容问题。
  4. useDefineForClassFields,推荐设置为true。由于TypeScript很早就引入了class字段,所以和目前的标准会有些不同,设置为true会按照最新标准来。这对于开发者是无感的,因为语法没有任何变化,只是runtime实现有区别。

Projects

这个部分推荐将composite设置为true,其他选项保持默认即可。关于此选项的解释可以参考官方手册

Completeness

这个部分只有一个字段,skipLibCheck,设置为true可以跳过类型检查节省编译的时间。我个人对于这个选项的见解有限,不过Vue.js官方的推荐配置以及Bun的推荐配置还有tsconfig社区的推荐配置都是设置为true,所以通常我也会设置为true。

Command Line

TypeScript官方的命令行工具有两个,分别是tsctsserver,前者是一个编译器,可以通过tsc -h查看帮助内容;后者是封装了编译器和语言服务的服务器,主要提供给IDE使用,提供类型补全和检查等能力。

总结

对于tsconfig的配置,我个人偏好严格一些,本文的推荐设置也是偏严格的,因为我选择使用TypeScript而不是JavaScript就是为了能够在代码运行之前发现一些问题。但是现实中每个人或团队的偏好不同,每个项目的需求也不同,最终还是要根据实际情况来修改配置。下面是根据上面的描述配置的一份适用于Web应用的tsconfig.json示例,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
  "include": ["src/**/*"],
  "exclude": ["src/**/__tests__/*"],
  // "extends": "@tsconfig/recommended/tsconfig.json",
  "compilerOptions": {
    "exactOptionalPropertyTypes": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "strict": true,
    "allowImportingTsExtensions": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "paths": { 
      "@/*": ["./src/*"]
    },
    "resolveJsonModule": true,
    "noEmit": true,
    "allowJs": true,
    "verbatimModuleSyntax": true,
    "esModuleInterop": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "jsx": "react-jsx",
    "lib": ["esnext"],
    "target": "es2020",
    "useDefineForClassFields": true,
    "composite": true,
    "skipLibCheck": true
  }
}