前言
配置 TSConfig 可以在写代码的同时发现错误,提高效率,规避低级错误,以及强大的类型系统,提升了代码的可维护性,使得重构代码更加容易等等;
一:安装 @vue/tsconfig
// 用于扩展Vue项目的tsconfig
pnpm add @vue/tsconfig --save-dev
二、配置 tsconfig.json 文件
// 参考文件:
// https://mp.weixin.qq.com/s/_sWdP9E4g-HJdtlcB_xamw
// https://www.tslang.cn/docs/handbook/compiler-options.html
// tsconfig.json 文件中指定了用来编译这个项目的根文件和编译选项
{
// 利用extends属性从另一个配置文件里继承配置
"extends": "@vue/tsconfig/tsconfig.dom.json",
// 编译器选项
"compilerOptions": {
// 指定 ECMAScript 目标版本 "ES3"(默认), "ES5", "ES6"/ "ES2015", "ES2016", "ES2017" 或 "ESNext"。
"target": "ESNext",
// 告诉 TypeScript 编译器你的代码运行的环境,并提供相应的类型定义。比如,如果你的代码运行在浏览器中,你可能需要包含 DOM 类型;如果你使用了 ES6 的特性,你需要包含 ES6 标准库等。
"lib": ["dom", "esnext", "DOM.Iterable"],
// 告诉 TypeScript 编译器不要包含任何默认的库文件(如 DOM、ES5、ES2015 等)。
"noLib": false, // 不排除默认库文件(比如 lib.d.ts)的类型检查。
// 参考文件: https://vitejs.cn/vite3-cn/guide/features.html#typescript
// 用于控制 TypeScript 如何处理类中的字段声明(class fields)。
// 当 "useDefineForClassFields": true 时,TypeScript 会使用 Object.defineProperty 来定义类字段,而不是直接在构造函数中赋值。
// 这可以确保类字段具有更精确的属性描述符(如不可写、不可枚举等),从而更符合 ECMAScript 标准提案。
"useDefineForClassFields": true,
// 用于启用对实验性装饰器(decorators)语法的支持
// "experimentalDecorators": true 允许你在 TypeScript 项目中使用装饰器语法(如 @Component、@Prop、@Inject 等),即使它们尚未成为 ECMAScript 的正式标准。
"experimentalDecorators": true,
// 用于启用对 JSON 模块的导入支持。
// 当 "resolveJsonModule": true 时,TypeScript 允许你像导入普通模块一样导入 .json 文件
"resolveJsonModule": true,
// 用于指定要包含在编译过程中的类型定义文件(.d.ts)。
// "types" 允许你手动指定要包含的类型定义模块,确保 TypeScript 编译器可以识别这些模块的类型信息。
// 它主要用于引入第三方库的类型定义,或者项目中自定义的全局类型声明。
// 与 "typeRoots" 不同,"types" 是指定具体的模块名(如 lodash、jquery),而不是指定包含类型定义的目录
// 在 Vue 3 + Vite 项目中,有时需要引入第三方库的类型定义,以获得更好的类型提示和编译时检查。
// 如果你使用了一些全局类型定义或第三方类型包(如 @types/node、@types/react 等),可以通过 "types" 显式声明它们
"types": ["node", "vite/client"],
// 用于允许在导入语句中使用 .ts 扩展名。
// 默认情况下,TypeScript 要求你在导入模块时省略 .ts 扩展名
// 当 "allowImportingTsExtensions": true 时,你可以显式地写上 .ts 扩展名
// 该配置对编译行为没有影响,只是放宽了导入路径的语法限制。
"allowImportingTsExtensions": true,
// 用于确保每个 TypeScript 文件都可以被单独编译,而不依赖于其他文件的内容。
// 这个选项主要是为了兼容像 Babel 或 esbuild 这类只做文件级别转译的工具,它们不会执行完整的程序类型检查
// 当 "isolatedModules": true 时,TypeScript 会强制执行以下限制:
// 1. 每个文件必须能独立编译,不能依赖其他文件的类型信息。
// 2. 不允许使用某些需要全局类型信息的语言特性(如 const enum、隐式类型导入等)。
"isolatedModules": true,
// 用于控制是否跳过对所有 *.d.ts 类型声明文件的类型检查。
// 当 "skipLibCheck": true 时,TypeScript 编译器会跳过对所有类型定义文件(如 node_modules/@types 下的 .d.ts 文件)的类型检查。
// 当 "skipLibCheck": false 时,TypeScript 会对所有 .d.ts 文件进行完整的类型检查
// 如果一个依赖项和 "isolatedModules": true 不兼容的话,
// 可以在上游仓库修复好之前暂时使用 "skipLibCheck": true 来缓解这个错误
"skipLibCheck": true,
// 启用所有严格类型检查选项; 启用 --strict相当于启用
// noImplicitAny: 在表达式和声明上有隐含的 any 类型时报错
// noImplicitThis: 当 this 表达式的值为 any 类型的时候,生成一个错误
// alwaysStrict: 以严格模式解析并为每个源文件生成 "use strict" 语句
// strictNullChecks: 在严格的 null 检查模式下, null 和 undefined 值不包含在任何类型里,只允许用它们自己和 any 来赋值(有个例外, undefined 可以赋值到 void)
// strictPropertyInitialization: 确保类的非 undefined 属性已经在构造函数里初始化。若要令此选项生效,需要同时启用 strictNullChecks
// strictFunctionTypes: 禁用函数参数双向协变检查。
"strict": true,
// 关闭: 在表达式和声明上有隐含的 any 类型时报错
"noImplicitAny": false,
// 关闭: 禁用函数参数双向协变检查。
"strictFunctionTypes": false,
// 决定了 TypeScript 怎么查找模块,
// 简单点说,就是告诉TypeScript:"当你看到import 'lodash'这种语句时,该去哪儿找这个模块"。
// 就像快递员送包裹,得知道是按门牌号逐户找(classic)还是直接查物业登记表(node)
// node: (现代项目首选)模拟Node.js的require()解析逻辑
// classic: (上古遗产)TypeScript早期的解析方式, 只傻傻地按相对路径查找,不会自动识别node_modules
// node16 / nodenext: Node.js的ESM模块解析规则,处理.mjs/.cjs扩展名区分(TypeScript 4.7+)
// bundler: 专为现代打包工具设计的模式,要求配合"module": "esnext"使用(TypeScript 5.0+)
"moduleResolution": "bundler",
// 指定生成哪个模块系统代码: "None", "CommonJS", "AMD", "System", "UMD", "ES6"或 "ES2015"。
// 只有 "AMD"和 "System" 能和 outFile 一起使用。
// "ES6"和 "ES2015"可使用在目标输出为 "ES5" 或更低的情况下。
"module": "ESNext",
// 解析非相对模块名的基准目录
// 不过要注意,这只是一个 TypeScript 的编译时特性。如果你用的打包工具(比如 webpack)不认识这个配置,还得在对应的配置里再加一遍。比如我们的 Vue 项目需要在 vite.config.ts 里加了 alias 配置才行。
"baseUrl": ".",
// 模块名到基于 baseUrl 的路径映射的列表
// 不过要注意,这只是一个 TypeScript 的编译时特性。如果你用的打包工具(比如 webpack)不认识这个配置,还得在对应的配置里再加一遍。比如我们的 Vue 项目需要在 vite.config.ts 里加了 alias 配置才行。
"paths": {
"@/*": ["src/*"]
},
// 允许编译 javascript 文件
"allowJs": true,
// 用于控制是否允许在派生类中隐式覆盖基类的方法或属性。
// 当 "noImplicitOverride": true 时,派生类中覆盖基类的方法或属性必须显式使用 override 关键字,否则 TypeScript 会报错
// 如果不启用该选项(即设为 false 或未设置),TypeScript 允许隐式覆盖,即不使用 override 也能覆盖基类成员。
"noImplicitOverride": true,
// 用于控制是否禁止定义但未使用的局部变量。
// 当 "noUnusedLocals": true 时,TypeScript 编译器会检查所有在函数或代码块中定义但从未被使用过的局部变量,并抛出编译错误。
// 如果 "noUnusedLocals": false 或未设置,则允许定义未使用的局部变量,不会报错
"noUnusedLocals": true,
// 用于控制是否禁止定义但未使用的函数参数。
// 当 "noUnusedParameters": true 时,TypeScript 编译器会检查函数中定义但从未被使用过的参数,并抛出编译错误。
// 如果 "noUnusedParameters": false 或未设置,则允许定义未使用的参数,不会报错。
"noUnusedParameters": false,
// 用于控制在 try/catch 语句中,catch 子句的变量类型是否为 unknown 而不是 any。
// 当 "useUnknownInCatchVariables": true 时,catch 语句中的错误变量类型会被推断为 unknown,这意味着你必须进行类型检查后才能使用该变量。
// 如果 "useUnknownInCatchVariables": false 或未设置,错误变量类型默认为 any,这会降低类型安全性。
"useUnknownInCatchVariables": false,
// 用于控制是否生成 .d.ts 声明文件的源码映射(source map)。
// 当 "declarationMap": true 时,TypeScript 编译器会为每个生成的 .d.ts 类型声明文件生成对应的 .d.ts.map 映射文件。
// 这些映射文件可以用于调试,帮助开发者在编辑器中跳转到原始 .ts 源代码的位置,而不是声明文件中的位置。
// 如果 "declarationMap": false 或未设置,则不会生成 .d.ts.map 文件。
"declarationMap": false,
// 用于控制是否将源代码内容嵌入到生成的 .js 文件的 source map 中。
// 当 "inlineSources": true 时,TypeScript 编译器会将源代码(即 .ts 文件的内容)直接嵌入到 .js.map 文件中。
// 这样做可以让开发者在调试时无需访问原始源文件,即可看到完整的源代码上下文。
// 如果 "inlineSources": false 或未设置,则 source map 中只会包含源文件路径,不会嵌入源代码内容。
"inlineSources": false,
// 用于控制是否生成编译后的 .js 文件和 .d.ts 类型声明文件。
// 当 "noEmit": true 时,TypeScript 不会生成任何输出文件(如 .js、.d.ts、.js.map 等),仅进行类型检查。
// 当 "noEmit": false 或未设置时,TypeScript 会正常生成编译后的 JavaScript 文件和相关资源。
// 这个选项非常适合仅进行类型检查而不执行实际编译的场景。
"noEmit": true,
// 用于控制是否在编译输出的 .js 文件中移除所有注释。
// 当 "removeComments": true 时,TypeScript 编译器会 删除所有注释(包括单行注释 // ... 和多行注释 /* ... */),生成的 .js 文件中将不包含任何注释。
// 当 "removeComments": false 或未设置时,TypeScript 会保留源代码中的注释。
"removeComments": true,
// 用于控制 TypeScript 是否允许从没有默认导出(default export)的模块中使用默认导入语法。
// 当 "allowSyntheticDefaultImports": true 时,TypeScript 允许你使用默认导入语法(import Module from 'module')来导入那些实际上没有默认导出的模块。
// 当 "allowSyntheticDefaultImports": false 时,TypeScript 会报错,要求你必须使用命名导入(import { Module } from 'module')来导入没有默认导出的模块。
"allowSyntheticDefaultImports": true,
// 用于 强制 TypeScript 文件名的大小写一致性检查。
// 当 "forceConsistentCasingInFileNames": true 时,TypeScript 会检查所有导入语句中的文件名大小写是否与实际文件路径一致。
// 如果导入路径中的大小写与实际文件不匹配,TypeScript 会抛出错误,防止因文件名大小写不一致导致的潜在问题。
// 这个选项主要用于跨平台开发中,避免在大小写敏感的系统(如 Linux、macOS)上出现模块导入错误。
"forceConsistentCasingInFileNames": true,
// 用于 控制在监视模式(--watch)下是否清除控制台输出。
// 当 "preserveWatchOutput": true 时,TypeScript 在监视模式下(如 tsc --watch)不会清除之前的编译输出,你可以在控制台中看到完整的编译历史记录。
// 当 "preserveWatchOutput": false 或未设置时,TypeScript 会在每次重新编译前清除控制台内容,只显示最新的编译信息。
"preserveWatchOutput": true,
},
// 如果 "include" 没有被指定,编译器默认包含当前目录和子目录下所有的 TypeScript 文件(.ts, .d.ts 和 .tsx),排除在"exclude"里指定的文件
// 如果 allowJs 被设置成 true, JS 文件(.js和.jsx)也被包含进来
"include": [
"**/*.d.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"types/**/*.d.ts",
"types/**/*.ts"
],
// 使用 "include" 引入的文件可以使用 "exclude" 属性过滤掉
"exclude": ["node_modules", "dist", "**/*.js", "**/*.md", "src/**/*.md"]
}
三、注意事项
注意1:
配置完 tsconfig.json 文件后, tsconfig.json 文件首行会提示一个报错信息,报错信息如下:
在配置文件“d:/xxx/xxx/xxx/tsconfig.json”中找不到任何输入。指定的 "include" 路径为“["**/*.d.ts","src/**/*.ts","src/**/*.tsx","src/**/*.vue","types/**/*.d.ts","types/**/*.ts","src/main.js"]”,"exclude" 路径为“["node_modules","dist","**/*.js","**/*.md","src/**/*.md"]”。
问题原因:
在 tsconfig.json 中的 include 没有包含 .js 文件,并且在 exclude 中将 .js 文件都排除掉了, 而我们项目的入口文件却又是 main.js 文件,因此,才会出现无法找到任何输入的错误。
解决方案:
将 main.js 文件改为 main.ts 文件即可;
注意2:
在配置完 tsconfig.json 文件的 "types": ["node", "vite/client"] 后,tsconfig.json 文件首行会提示一个报错信息,报错信息如下:
找不到“node”的类型定义文件。
程序包含该文件是因为:
在 compilerOptions 中指定的类型库 "node" 的入口点t
问题原因:
找不到 node 的类型定义文件,即还没有安装 @types/node 依赖包;
解决方案:
// 用于扩展Vue项目的tsconfig
pnpm add @types/node --save-dev
注意3:(参考文献:Vite官方中文文档-功能-客户端类型)
在配置完 tsconfig.json 文件的 "types": ["node", "vite/client"] 后,需要新建一个 types/vite-client.d.ts 文件,文件内容如下:
/// <reference types="vite/client" />
vite/client 会提供以下类型定义补充:
- 资源导入 (例如:导入一个 .svg 文件)
- import.meta.env 上 Vite 注入的 常量 的类型定义
- import.meta.hot 上的 HMR API 类型定义
添加 types/vite-client.d.ts 文件的原因如下:
Vite 默认的类型定义是写给它的 Node.js API 的。要将其补充到一个 Vite 应用的客户端代码环境中,需要添加一个 d.ts 声明文件,
同时,你也可以将 vite/client 添加到 tsconfig 中的 compilerOptions.types 中 例如:
{
"compilerOptions": {
"types": ["vite/client"]
}
}
这将会提供以下类型定义补充:
- 资源导入 (例如:导入一个 .svg 文件)
- import.meta.env 上 Vite 注入的环境变量的类型定义
- import.meta.hot 上的 HMR API 类型定义
注意4:
在配置完 tsconfig.json 文件的 "baseUrl": "." 和 "paths": { "@/": ["src/"] } 后,需要在 vite.config.ts 里加了 alias 配置才行
import { resolve } from 'node:path'
defineConfig({
resolve: {
alias: [
{
find: '@',
replacement: resolve(__dirname, './src'), // TODO: resolve 是一个函数,需要从 node 中导入
},
],
}
})