你真的掌握了 tsconfig 中的 type 吗?

586 阅读5分钟

types 是 tsconfig 文件中的一个配置,默认情况下,node_modules 下面的 @types 包都会包含在项目的编译过程中。如果指定了 types ,那么只有在 types 中列出的包才会被包含在全局作用域内。

例如下面的配置:

{
  "compilerOptions": {
    "types": ["node", "jest", "express"]
  }
}

则此项目的全局作用域将只会包含 ./node_modules/@types/node./node_modules/@types/jest./node_modules/@types/express 。其他在 node_modules/@types/* 下的包不会包含在全局作用域中。

tsconfig.json 中设置了 types 后对项目会有什么影响?

此选项不会影响 @types/* 如何被包含在你项目的代码中,例如你在有上面例子里 compilerOptions 的环境中写了这样的代码:

import moment from "moment"

moment().format("MMMM Do YYYY, h:mm:ss a")

moment 导入会有完整的类型。

当你设置了这个选项,通过不在类型数组中包含模块:

  • 不会向你的项目中添加全局变量(例如 Node 环境中的 process,或者 Jest 框架中的expect

  • 不会让导出内容以自动导入推荐的形式出现

例如在下面的 types 配置中:

{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

23.png

如果在 types 配置中加上 jest ,就不会有上图中的报错了,TypeScript 可以正确地识别出 expect 的类型:

{
  "compilerOptions": {
    "types": ["vite/client", "jest"]
  }
}

24.png

还是上面的配置,当 types 中没有加入 moment 时,直接在 TypeScript 代码中使用 moment ,一方面无法识别 moment 类型,另一方面不会有导入推荐提示:

25.png

当在 types 配置加上 moment 后,TypeScript 就能识别出 moment ,并有导入推荐提示:

{
  "compilerOptions": {
    "types": ["vite/client", "jest", "moment"]
  }
}

26.png

27.png

types 配置默认情况下会读取 node_modules 下的 @types 中的包,然后将其加入全局作用域中,方便开发者引入 @types 下的包声明的类型。

如果指定了 types ,则 TypeScript 只会把 types 中列出的包加入全局作用域和导入推荐提示。

通过 types 配置,可以精确的控制全局范围的类型定义,从而避免类型冲突。比如,假设有两个库 AB ,它们都有一个名为 SomeType 的类型定义,并且这些定义可能会相互冲突。你可以通过 types 配置只包含你需要的那个库的类型定义,避免冲突。

虽然 types 配置控制的是全局范围的类型定义,但这并不意味着未包含在 types 列表中的 @types 包完全不能使用。在模块内部,你仍然可以通过导入的方式使用未在 types 列表中的 @types 包的类型定义。

例如下面的配置,没有引入 express

{
  "compilerOptions": {
    "types": ["vite/client", "jest"]
  },
}

但是在 TypeScript 代码中仍可通过导入 express 包来使用在 @types/express 中的类型定义:

import { RouterOptions } from "express";

const obj1: RouterOptions = {}

TypeScript 编译器会先在当前编译上下文找相关的类型定义,如果找不到,再到 node_modules 中的 @types(默认情况,目录可以修改)目录下去寻找对应包名的模块声明。

注意,有些 npm 的类型定义没有 @types 包,同时不能直接在 TypeScript 文件中直接引入 @types 下的包,因为 @types 下的包只是提供类型信息,而实际的功能需要通过导入对应的库来完成。

28.png

指定 types 配置可以提升 TypeScript 的编译性能,如果只包含必要的 @types 包,编译器就不需要处理大量不必要的类型信息,从而可以提供编译速度。types 配置有助于增强类型安全性。通过明确列出需要的类型定义,你可以更好地控制项目的类型依赖关系,避免意外地使用了不应该使用的类型定义,从而减少类型错误的发生。

tsconfig 中的 types 配置用于控制在全局范围内包含的类型定义(主要 node_modules 下的 @types 包),和默认包含所有可见 @types 包的情况不同,它只允许 types 列出的包在全局可用,能精确控制全局类型范围、避免类型冲突,提升编译性能和类型安全性,同时未在列表中的 @types 包仍可在模块内通过导入使用(注意,不能直接在 ts 文件中直接导入 @types 包下的模块)

在 tsconfig 的配置中,还有个属性叫 typeRoots ,我们前面说过,默认情况下,TypeScript 编译器会先在当前编译上下文找相关的类型定义,如果找不到,再到 node_modules 中的 @types 中寻找,而设置了 typeRoots 后,则 TypeScript 编译器不会在 node_modules 下的 @types 中寻找类型定义了,而是在 typeRoots 中列出的路径下查找类型定义。例如:

{
  "compilerOptions": {
    "typeRoots": ["./typings", "./vendor/types"]
  }
}

这个配置文件将包含 ./typings./vendor/types 下的所有包,而不包括 ./node_modules/@types 下的。其中所有的路径都是相对于 tsconfig.json

如下面的 tsconfig 配置:

{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

TypeScript 会将 vite/client 中的类型加到全局作用域中,这样 TypeScript 编译器能够识别 Vite 客户端相关的类型定义,从而可以在代码中利用这些类型进行更准确的类型检查、代码提示等操作,避免类型相关的错误。

总结

types 是 tsconfig 文件中的一个配置,如果没有指定 types ,则 TypeScript 会将 node_modules 下所有 @types 下面的类型定义包都加入全局作用域下。

如果指定了 types ,则只会将 types 列出的包加入全局作用域。而没有在 types 中列出的类型定义包,只能通过在 ts 代码中通过导入来使用。

设置 types 有助于避免类型冲突,提升编译性能和类型安全。

typeRootstypes 的区别是,默认情况下,TypeScript 编译器会先在当前编译上下文找相关的类型定义,如果找不到,再到 node_modules 中的 @types 中寻找,而设置了 typeRoots 后,则 TypeScript 编译器不会在 node_modules 下的 @types 中寻找类型定义了,而是在 typeRoots 中列出的路径下查找类型定义。

typeRootstypes 可以互相配合使用,当设置了 typeRoots 后,types 列表中的包需要从 typeRoots 指定的文件夹下查找,否则 tsconfig 文件会报错:

29.png