掌握 tsconfig.json
如果你使用VSCode,通常每个项目根目录下都会有一个jsconfig.json或tsconfig.json文件,那这个文件的作用是什么呢?
按官方文档的解释来说,jsconfig.json标记该目录是JS项目根目录,并根据内部配置为项目内支持类型的文件提供语言服务(例如api提示、导入路径提示等)
因为ts文件相关配置无需在js项目中配置,所以官方称jsconfig属于tsconfig的子集,后文中均以tsconfig做说明
为什么需要
- 没有
tsconfig.json时,VSCode将每个文件都看作独立的单元。只要两个文件间没有通过模块导入语句显示的引用,这两个文件就没有公共的项目上下文 - 有
tsconfig.json时,配置的属性会作用到项目内的每个文件,例如可以选择性的列出语言服务需要支持哪些文件或不支持哪些文件
简单来说就是有了tsconfig.json文件后,设置的include、exclude等属性能够约束语言服务的作用范围。例如在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
指定允许被识别的文件列表,如果找不到对应文件会报错
用于只有少量文件需要被识别时取代include,files与include也可以同时存在。另外files指定的文件不会被exclude排除
exclude
指定不应该被识别的文件或文件夹列表,同样可以使用通配符
exclude会默认排除node_modules、bowser_components、jspm_packages和outDir属性指定的目录
提示
未指定时,include默认值为["**/*"];files、exclude默认为[]
extends
{
"extends": "./anotherConfig.json"
}{
"extends": "./anotherConfig.json"
}继承另一个配置文件中的配置,路径匹配规则采用NodeJS匹配规则。主配置文件中的配置属性会覆盖extends指定文件中的属性,对象中的不同属性会合并后作为完整的配置。多个配置文件不能通过extends属性循环引用
需要注意files、include、exclude字段不会发生合并,优先使用主配置文件中的配置
watchOptions
vueCompilerOptions
TS 相关属性
compilerOptions
compilerOptions是TS配置的主要部分
allowUnreachableCode
定义如何处理不会被访问到的代码,默认undefined表示展示警告,还支持true表示忽略,false表示报错。例如:
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都包含break或return
noImplicitOverride
开启后子类重写父类方法必须添加override关键字:
class Son extends Father {
override func() {}
}class Son extends Father {
override func() {}
}noImplicitReturns
启用后将严格检查函数是否有明确的返回值(默认的返回undefined会报错)
noPropertyAccessFromIndexSignature
开启后将不能用点语法(obj.attr)访问对象中通过索引定义的属性:
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
开启后将检查call、apply、bind中的参数类型是否与原函数定义的类型一致
strictFunctionTypes
开启后将使函数参数类型检查更加准确,例如一个更精确的函数类型不能赋值给类型的扩展集:
function fn(x: string) {
console.log('Hello, ' + x.toLowerCase())
}
type StringOrNumberFunc = (ns: string | number) => void
// Unsafe assignment is prevented
let func: StringOrNumberFunc = fnfunction fn(x: string) {
console.log('Hello, ' + x.toLowerCase())
}
type StringOrNumberFunc = (ns: string | number) => void
// Unsafe assignment is prevented
let func: StringOrNumberFunc = fnstrictNullChecks
开启后null和undefined将看作具体的类型,而不会默认为其他类型的子类型
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类型)会报错,例如:
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选项相当于开启了如下的八个编译选项:
- alwaysStrict
- strictBindCallApply
- strictFunctionTypes
- strictNullChecks
- strictPropertyInitialization
- noImplicitAny
- noImplicitThis
- useUnknownInCatchVariables
设置strict: true后,这些选项也可以单独设置
allowUmdGlobalAccess
开启后允许以全局变量的形式访问UMD模块导出
baseUrl
定义相对路径解析时相对的基准目录
默认为./即相对于根目录,例如设置为src/后,项目内的相对路径将从src开始查找
module
定义项目使用的模块系统,支持CommonJS, AMD, System, UMD, ES6, ES2015, ES2020, ESNext, None, ES2022, Node16, NodeNext
一般只需要设置为CommonJS或ESNext,这个属性决定了ts文件编译后代码需要将模块化语句转换为何种模块化格式
moduleResolution
定义编译器查找模块导入文件的策略,支持Classic或Node。未指定时如果使用了module为AMD或System或ES2015时为Classic,其他情况视为Node
Classic策略是早期TS默认的解析策略:相对导入时会依次查找相对目录下的.ts、.d.ts文件;非相对导入时会从当前目录依次遍历至根目录(baseUrl)查找.ts、.d.ts文件
Node策略是模仿NodeJS的模块解析机制,不同的是TS项目增加了扩展名的支持(.ts、.tsx、.d.ts,如果设置了allowJs则还包括.js和.jsx)
moduleSuffixes
用于在解析模块时添加默认后缀名进行搜索,例如:
"moduleSuffixes": [".ios", ".native", ""]
import * as foo from "./foo";"moduleSuffixes": [".ios", ".native", ""]
import * as foo from "./foo";此时会按照foo.ios.ts、foo.native.ts、foo.ts的顺序进行搜索(此处省略了tsx等扩展名替换步骤)