在使用Vite创建项目时使用的是vue-ts模板,所以在创建项目的时候package.json就自带了typescript

在初始化 vite 项目的时候就可以看到,项目根目录下生成了 tsconfig.jsontsconfig.node.json 两个文件。

1.tsconfig.json 配置项

在初始化 vite 项目的时候可以看到,项目根目录下生成了 tsconfig.jsontsconfig.node.json 两个文件。

tsconfig.json 指定了用来编译这个项目的根文件和编译选项

  1. {
  2. "compilerOptions": {
  3. "target": "esnext",
  4. "useDefineForClassFields": true,
  5. "module": "esnext",
  6. "moduleResolution": "node",
  7. "strict": true,
  8. "jsx": "preserve",
  9. "sourceMap": true,
  10. "resolveJsonModule": true,
  11. "esModuleInterop": true,
  12. "lib": ["esnext", "dom"]
  13. },
  14. "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  15. "references": [{ "path": "./tsconfig.node.json" }]
  16. }

可以看到tsconfig.node.json中的"include": ["vite.config.ts"]表明这个单独拆分出来的配置文件只是负责编译 vite 的配置文件vite.config.ts

  1. {
  2. "compilerOptions": {
  3. "composite": true,
  4. "module": "esnext",
  5. "moduleResolution": "node"
  6. },
  7. "include": ["vite.config.ts"]
  8. }

include 指定需要编译的文件范围

默认是对 src 目录下的.ts、.d.ts、.tsx、.vue结尾的文件都需要进行编译,这里扩展下和src同级的tests、build目录(如果项目中需要的话)

  1. "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "build/**/*.ts", "tests/**/*.ts", "tests/**/*.tsx"]

exclude 配置不需要编译的文件范围

  1. "exclude": ["node_modules", "tsconfig.json", "dist"]

references 将 TypeScript 程序拆分

references 是 TypeScript 3.0 的新特性,允许将 TypeScript 程序拆分结构化。这和我们写 vue 组件的思想有着异曲同工之妙,避免了在一个文件中处理过多的内容,而是通过拆分成多个文件,分别配置不同的部分,达到更加清晰,可维护性更高的目的。

这里我们可以不拆分,可以删除掉tsconfig.node.json统一用tsconfig.json,并移除默认的:

  1. "references": [{ "path": "./tsconfig.node.json" }]

compilerOptions 编译选项

useDefineForClassFields

useDefineForClassFields 是 TypeScript 3.7.0 中新增的一个编译选项,启用后的作用是将 class 声明中的字段语义从 [[Set]] 变更到 [[Define]]

skipLibCheck 忽略声明文件检查

忽略所有的声明文件( *.d.ts)的类型检查

  1. "compilerOptions": {
  2. "skipLibCheck": true
  3. }

这个属性不但可以忽略 npm 不规范带来的报错,还能最大限度的支持类型系统。设置为 true 就不用怕使用的第三方库不规范了。

为什么要跳过这些第三方库的检查?

我们做类型检查(以及代码规范等)的目的是为了对团队内业务代码开发保持统一和规范,以保证开发成员之间的快速上手和后续维护。所以我们要做的是将各种规则集成到业务代码模块,而一些框架上的或者第三方库的内容就不用多此一举了。

baseUrl

作用:设置baseUrl来告诉编译器到哪里去查找模块。所有非相对模块导入都会被当做相对于 baseUrl

注意相对模块的导入不会被设置的baseUrl所影响,因为它们总是相对于导入它们的文件.
这个设置的作用和 vite 构建选项 build 中的 base 是类似的,我们配置为当前根目录即可

paths 路径映射列表

作用:模块名到基于baseUrl的路径映射的列表。

请注意"paths"是相对于"baseUrl"进行解析。

在 vite 配置中设置了路径别名resolve.alias,为了让编译 ts 时也能够解析对应的路径,我们还需要配置 paths 选项

  1. "paths": {
  2. "@/*": ["src/*"],
  3. "utils/*": ["src/utils/*"],
  4. "api/*": ["src/api/*"],
  5. "components/*": ["src/components/*"],
  6. "views/*": ["src/views/*"],
  7. "#/*": ["types/*"],
  8. "build": ["build/*"]
  9. },

isolatedModules 将每个文件作为单独模块

typescript 将没有导入/导出的文件视为旧脚本文件。因为这样的文件不是模块和它们在全局命名空间中合并的任何定义。该配置项会禁止此类文件。将任何导入或导出添加的文件都视为模块

这个设置在 vite 官方文档中是被要求应该设置为 true 的,但(vite2)项目初始化的时候并没有默认加上这条(vite3加上了)

  1. "isolatedModules": true,

image.png
https://vitejs.cn/guide/features.html#typescript-compiler-options

types 客户端类型

作用:添加要包含的类型声明文件名列表,只有在这里列出的模块的声明文件才会被加载进来

  1. {
  2. "compilerOptions": {
  3. "types": ["vite/client"]
  4. }
  5. }

这将会提供以下类型定义补充:

  • 资源导入 (例如:导入一个 .svg 文件)
  • import.meta.env 上 Vite 注入的环境变量的类型定义
  • import.meta.hot 上的 HMR API 类型定义

tsconfig.json文件首行报错

vscode会自动进行文件的语义检查。因为自定义的tsconfig.json文件无法覆盖vscode自带的配置,所以会报错。

打开配置settings.json文件,让自定义的jsconfig.js文件覆盖vscode默认选项。
image.png

3.2. env.d.ts 全局类型声明

打开 src 文件夹下的 env.d.ts(vite3为vite-env.d.ts)文件,可以看到

  1. /// <reference types="vite/client" />
  2. declare module '*.vue' {
  3. import type { DefineComponent } from 'vue'
  4. // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
  5. const component: DefineComponent<{}, {}, any>
  6. export default component
  7. }

image.png
请注意:三斜杠指令是包含单个XML标签的单行注释,注释的内容会做为编译器指令使用,只有在文件的最顶部才会生效,可以查看TypeScript: Documentation - Triple-Slash Directives学习相关内容。

所以这个文件的作用就呼之欲出了:帮助编译器识别类型
TypeScript 相比 JavaScript 增加了类型声明。原则上,TypeScript 需要做到先声明后使用。这就导致开发者在调用很多原生接口(浏览器、Node.js)或者第三方模块的时候,因为某些全局变量或者对象的方法并没有声明过,导致编译器的类型检查失败。

当我们遇上Property xxx does not exist on type …报错的时候,就可以定位到是没有声明过这个方法/属性。我们就可以在这个文件作全局声明。

可以看到,该文件已经默认为所有的 vue 文件声明了 DefineComponent的组件类型,这就意味着只要我们的单文件组件使用

  1. <script lang="ts">
  2. import { defineComponent } from 'vue'
  3. export default defineComponent({
  4. ...
  5. })
  6. </script>

的写法,就能避免 vue 文件中大多数类型声明的报错,比如使用路由的this.$router、this.$route命令

2. 完整配置文件

原代码

  1. {
  2. "compilerOptions": {
  3. "target": "ESNext",
  4. "useDefineForClassFields": true,
  5. "module": "ESNext",
  6. "importHelpers": true,
  7. "moduleResolution": "Node",
  8. "experimentalDecorators": true,
  9. "allowSyntheticDefaultImports": true,
  10. "strict": true,
  11. "jsx": "preserve",
  12. "sourceMap": true,
  13. "resolveJsonModule": true,
  14. "isolatedModules": true,
  15. "esModuleInterop": true,
  16. "baseUrl": ".",
  17. "types": ["vite/client"],
  18. "noEmit": true,
  19. "paths": {
  20. "@/*": ["src/*"],
  21. "utils/*": ["src/utils/*"],
  22. "api/*": ["src/api/*"],
  23. "components/*": ["src/components/*"],
  24. "#/*": ["types/*"],
  25. "build": ["build/*"]
  26. },
  27. "lib": ["ESNext", "DOM"],
  28. "skipLibCheck": true,
  29. "typeRoots": ["./node_modules/@types/", "./types"]
  30. },
  31. "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "build/**/*.ts", "tests/**/*.ts", "tests/**/*.tsx"],
  32. "exclude": ["node_modules", "tsconfig.json", "dist"]
  33. }

带注释代码

  1. {
  2. "compilerOptions": {
  3. // ↓指定ECMAScript目标版本,esnext为最新版本
  4. "target": "ESNext",
  5. // ↓将 class 声明中的字段语义从 [[Set]] 变更到 [[Define]]
  6. "useDefineForClassFields": true,
  7. // ↓指定生成哪个模块系统代码,esnext为最新版本
  8. "module": "ESNext",
  9. "importHelpers": true,
  10. // ↓决定如何处理模块。
  11. "moduleResolution": "Node",
  12. // ↓启用实验性的ES装饰器。
  13. "experimentalDecorators": true,
  14. // ↓允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
  15. "allowSyntheticDefaultImports": true,
  16. // ↓启用所有严格类型检查选项。
  17. "strict": true,
  18. // ↓在 .tsx文件里支持JSX
  19. "jsx": "preserve",
  20. // ↓有未使用的局部变量抛错。
  21. // "noUnusedLocals": true,
  22. // ↓有未使用的参数抛错。
  23. // "noUnusedParameters": true,
  24. // ↓生成相应的 .map文件。
  25. "sourceMap": true,
  26. "resolveJsonModule": true,
  27. //将每个文件作为单独模块
  28. "isolatedModules": true,
  29. "esModuleInterop": true,
  30. // ↓解析非相对模块名的基准目录。
  31. "baseUrl": ".",
  32. // ↓要包含的类型声明文件名列表。
  33. "types": ["vite/client"],
  34. "noEmit": true,
  35. // ↓模块名到基于 baseUrl的路径映射的列表。
  36. "paths": {
  37. "@/*": ["src/*"],
  38. "utils/*": ["src/utils/*"],
  39. "api/*": ["src/api/*"],
  40. "components/*": ["src/components/*"],
  41. "views/*": ["src/views/*"],
  42. "#/*": ["types/*"],
  43. "build": ["build/*"]
  44. },
  45. // ↓编译过程中需要引入的库文件的列表。
  46. "lib": ["ESNext", "DOM"],
  47. // ↓忽略所有的声明文件( *.d.ts)的类型检查。
  48. "skipLibCheck": true,
  49. // ↓要包含的类型声明文件路径列表。
  50. "typeRoots": ["./node_modules/@types/", "./types"],
  51. },
  52. // ↓指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
  53. "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "build/**/*.ts", "tests/**/*.ts", "tests/**/*.tsx"],
  54. // 指定一个排除列表(include的反向操作)
  55. "exclude": ["node_modules", "tsconfig.json", "dist"]
  56. }