Skip to content

掌握 tsconfig.json

如果你使用VSCode,通常每个项目根目录下都会有一个jsconfig.jsontsconfig.json文件,那这个文件的作用是什么呢?

官方文档的解释来说,jsconfig.json标记该目录是JS项目根目录,并根据内部配置为项目内支持类型的文件提供语言服务(例如api提示、导入路径提示等)

因为ts文件相关配置无需在js项目中配置,所以官方称jsconfig属于tsconfig的子集,后文中均以tsconfig做说明

为什么需要

  1. 没有tsconfig.json时,VSCode将每个文件都看作独立的单元。只要两个文件间没有通过模块导入语句显示的引用,这两个文件就没有公共的项目上下文
  2. tsconfig.json时,配置的属性会作用到项目内的每个文件,例如可以选择性的列出语言服务需要支持哪些文件或不支持哪些文件

简单来说就是有了tsconfig.json文件后,设置的includeexclude等属性能够约束语言服务的作用范围。例如在a.js中使用console.log(b)时,因为文件上下文中没有b,所以编辑器提供的语言服务会去查找有没有哪一个文件导出了b,如果项目非常巨大的话,全目录查找效率会很低下,这时候就需要tsconfig.json来指定哪部分文件才是需要查找的,从而提高编辑器效率


另外我们知道可以用typescript库提供的tsc命令编译输出js文件,少量文件编译可以通过参数指定编译选项,但如果编译整个项目则需要利用tsconfig.json来指定编译选项,同时统一编译策略也利于团队协作开发

tsc命令行中指定的选项会始终覆盖tsconfig.json中的对应配置

创建文件

tsconfig.json文件可以手动创建,在vscode中通过触发建议快捷键即可提示相关属性

tsconfig.json还可以通过tsc init命令生成(需要先安装typescript包),生成的内容只有compilerOptions字段,并开启了几个常用属性,其他属性也通过注释做了详细的解释

tsconfig.json文件可以是个空文件,只要创建了这个文件,所在目录就会被识别为项目根目录,相关属性会使用默认值

JS、TS 通用属性

include

指定允许被识别的文件或文件夹列表,运行使用通配符:

  • *匹配 0 个或多个字符(不包括目录分隔符)
  • ?匹配任意一个字符(不包括目录分隔符)
  • **/匹配任意一级或多级目录

如果路径某一部分只包含*.*,那么这部分只会匹配支持的扩展名(.ts.tsx.d.ts,如果设置了allowJs则还包括.js.jsx

files

指定允许被识别的文件列表,如果找不到对应文件会报错

用于只有少量文件需要被识别时取代includefilesinclude也可以同时存在。另外files指定的文件不会被exclude排除

exclude

指定不应该被识别的文件或文件夹列表,同样可以使用通配符

exclude会默认排除node_modulesbowser_componentsjspm_packagesoutDir属性指定的目录

提示

未指定时,include默认值为["**/*"]filesexclude默认为[]

extends

json
{
  "extends": "./anotherConfig.json"
}
{
  "extends": "./anotherConfig.json"
}

继承另一个配置文件中的配置,路径匹配规则采用NodeJS匹配规则。主配置文件中的配置属性会覆盖extends指定文件中的属性,对象中的不同属性会合并后作为完整的配置。多个配置文件不能通过extends属性循环引用

需要注意filesincludeexclude字段不会发生合并,优先使用主配置文件中的配置

watchOptions

vueCompilerOptions

TS 相关属性

compilerOptions

compilerOptionsTS配置的主要部分

allowUnreachableCode

定义如何处理不会被访问到的代码,默认undefined表示展示警告,还支持true表示忽略,false表示报错。例如:

ts
function fn(n: number) {
  if (n > 5) {
    return true
  } else {
    return false
  }
  // 下面的代码永远不会被执行到
  return true
}
function fn(n: number) {
  if (n > 5) {
    return true
  } else {
    return false
  }
  // 下面的代码永远不会被执行到
  return true
}

allowUnusedLabels

定义如何处理未使用的标签,规则同allowUnreachableCode。标签语法并不常用,若不了解可以参考MDN 文档

exactOptionalPropertyTypes

是否需要精确定义可选属性类型。默认TS可选属性可以为undefined,如果开启此选项则不会默认支持undefined

noFallthroughCasesInSwitch

是否需要switch语句中每个非空case都包含breakreturn

noImplicitOverride

开启后子类重写父类方法必须添加override关键字:

ts
class Son extends Father {
  override func() {}
}
class Son extends Father {
  override func() {}
}

noImplicitReturns

启用后将严格检查函数是否有明确的返回值(默认的返回undefined会报错)

noPropertyAccessFromIndexSignature

开启后将不能用点语法(obj.attr)访问对象中通过索引定义的属性:

ts
interface test {
  a: string
  [key: string]: string
}

let obj: test = { a: 'a' }
// 开启noPropertyAccessFromIndexSignature后将报错
// 属性“b”来自索引签名,因此必须使用[“b”]访问它。ts(4111)
console.log(obj.b)
interface test {
  a: string
  [key: string]: string
}

let obj: test = { a: 'a' }
// 开启noPropertyAccessFromIndexSignature后将报错
// 属性“b”来自索引签名,因此必须使用[“b”]访问它。ts(4111)
console.log(obj.b)

noUncheckedIndexedAccess

开启后

noUnusedLocals

开启后未使用的局部变量会报错

noUnusedParameters

开启后未使用的函数参数会报错

alwaysStrict

是否将每个文件都看作开启严格模式(use strict),在ESModule文件中会默认开启,具体规则可以参考MDN 文档

strictBindCallApply

开启后将检查callapplybind中的参数类型是否与原函数定义的类型一致

strictFunctionTypes

开启后将使函数参数类型检查更加准确,例如一个更精确的函数类型不能赋值给类型的扩展集:

ts
function fn(x: string) {
  console.log('Hello, ' + x.toLowerCase())
}

type StringOrNumberFunc = (ns: string | number) => void

// Unsafe assignment is prevented
let func: StringOrNumberFunc = fn
function fn(x: string) {
  console.log('Hello, ' + x.toLowerCase())
}

type StringOrNumberFunc = (ns: string | number) => void

// Unsafe assignment is prevented
let func: StringOrNumberFunc = fn

strictNullChecks

开启后nullundefined将看作具体的类型,而不会默认为其他类型的子类型

ts
let a: string
// 默认null和undefined可以赋值给其他类型,开启strictNullChecks后将会报错
a = undefined
// 未开启strictNullChecks时b的类型别识别为string
// 开启后会识别为string|undefined
let a: string[] = []
let b = a.find((item) => item == 'a')
let a: string
// 默认null和undefined可以赋值给其他类型,开启strictNullChecks后将会报错
a = undefined
// 未开启strictNullChecks时b的类型别识别为string
// 开启后会识别为string|undefined
let a: string[] = []
let b = a.find((item) => item == 'a')

strictPropertyInitialization

开启后如果类中声明了类属性,但未在构造函数中设置该属性时会报错

noImplicitAny

是否允许不明确类型的变量存在。例如函数参数如果未指定类型,TS会将类型看作any,该属性定义是否允许这种默认的回退类型行为

noImplicitThis

开启后对this类型无法识别时(隐式any类型)会报错,例如:

ts
class Name {
  firstName: ''
  lastName: ''

  outputName() {
    return function () {
      // 这个函数中的this在调用时才能确定,而不是Name类,会报错
      return this.firstName + this.lastName
    }
  }
}
class Name {
  firstName: ''
  lastName: ''

  outputName() {
    return function () {
      // 这个函数中的this在调用时才能确定,而不是Name类,会报错
      return this.firstName + this.lastName
    }
  }
}

useUnknownInCatchVariables

开启后catch中的错误类型会由any改为unknown

strict

开启严格模式,截至TS 4.3开启strict选项相当于开启了如下的八个编译选项:

  1. alwaysStrict
  2. strictBindCallApply
  3. strictFunctionTypes
  4. strictNullChecks
  5. strictPropertyInitialization
  6. noImplicitAny
  7. noImplicitThis
  8. useUnknownInCatchVariables

设置strict: true后,这些选项也可以单独设置

allowUmdGlobalAccess

开启后允许以全局变量的形式访问UMD模块导出

baseUrl

定义相对路径解析时相对的基准目录

默认为./即相对于根目录,例如设置为src/后,项目内的相对路径将从src开始查找

module

定义项目使用的模块系统,支持CommonJS, AMD, System, UMD, ES6, ES2015, ES2020, ESNext, None, ES2022, Node16, NodeNext

一般只需要设置为CommonJSESNext,这个属性决定了ts文件编译后代码需要将模块化语句转换为何种模块化格式

moduleResolution

定义编译器查找模块导入文件的策略,支持ClassicNode。未指定时如果使用了moduleAMDSystemES2015时为Classic,其他情况视为Node

Classic策略是早期TS默认的解析策略:相对导入时会依次查找相对目录下的.ts.d.ts文件;非相对导入时会从当前目录依次遍历至根目录(baseUrl)查找.ts.d.ts文件

Node策略是模仿NodeJS的模块解析机制,不同的是TS项目增加了扩展名的支持(.ts.tsx.d.ts,如果设置了allowJs则还包括.js.jsx)

moduleSuffixes

用于在解析模块时添加默认后缀名进行搜索,例如:

js
"moduleSuffixes": [".ios", ".native", ""]

import * as foo from "./foo";
"moduleSuffixes": [".ios", ".native", ""]

import * as foo from "./foo";

此时会按照foo.ios.tsfoo.native.tsfoo.ts的顺序进行搜索(此处省略了tsx等扩展名替换步骤)