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 文件会报错: