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"]
}
}
如果在 types 配置中加上 jest ,就不会有上图中的报错了,TypeScript 可以正确地识别出 expect 的类型:
{
"compilerOptions": {
"types": ["vite/client", "jest"]
}
}
还是上面的配置,当 types 中没有加入 moment 时,直接在 TypeScript 代码中使用 moment ,一方面无法识别 moment 类型,另一方面不会有导入推荐提示:
当在 types 配置加上 moment 后,TypeScript 就能识别出 moment ,并有导入推荐提示:
{
"compilerOptions": {
"types": ["vite/client", "jest", "moment"]
}
}
types 配置默认情况下会读取 node_modules 下的 @types 中的包,然后将其加入全局作用域中,方便开发者引入 @types 下的包声明的类型。
如果指定了 types ,则 TypeScript 只会把 types 中列出的包加入全局作用域和导入推荐提示。
通过 types 配置,可以精确的控制全局范围的类型定义,从而避免类型冲突。比如,假设有两个库 A 和 B ,它们都有一个名为 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 下的包只是提供类型信息,而实际的功能需要通过导入对应的库来完成。
指定 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 有助于避免类型冲突,提升编译性能和类型安全。
typeRoots 和 types 的区别是,默认情况下,TypeScript 编译器会先在当前编译上下文找相关的类型定义,如果找不到,再到 node_modules 中的 @types 中寻找,而设置了 typeRoots 后,则 TypeScript 编译器不会在 node_modules 下的 @types 中寻找类型定义了,而是在 typeRoots 中列出的路径下查找类型定义。
typeRoots 和 types 可以互相配合使用,当设置了 typeRoots 后,types 列表中的包需要从 typeRoots 指定的文件夹下查找,否则 tsconfig 文件会报错: