@types 与 DefinitelyTyped
本教程共 80 篇 · 第 65 篇 · 更新于 2026-08-10 · 约 13 分钟阅读
本节目标:了解 @types 包体系的全貌——它从哪来、怎么用、版本号为什么那样定,以及如何向 DefinitelyTyped 社区贡献类型定义。学完你再遇到缺少类型的 npm 包,就知道怎么找、怎么装,甚至怎么给社区提交。
没有 @types 之前的世界
早年写 TypeScript,最头疼的不是类型系统,而是「第三方库没类型」。比如你装了个 lodash,然后写:
import _ from "lodash";
// TypeScript:找不到模块"lodash"的声明文件。
那时候你得自己满世界找声明文件——有人放在 GitHub 仓库里,有人放在博客上,版本还对不上。更糟糕的是,同一个库可能有五六个人各写了一套互不兼容的声明,你选哪套都踩坑。
DefinitelyTyped 就是来解决这个问题的。
DefinitelyTyped 是什么
DefinitelyTyped(简称 DT)是一个社区维护的类型定义仓库,托管在 GitHub 上:
https://github.com/DefinitelyTyped/DefinitelyTyped
它是 GitHub 上最大的仓库之一,包含了超过 8000 个 npm 包的 TypeScript 类型定义。任何 npm 上流行但没有自带类型的 JS 库,你大概率能在这里找到对应的 @types/xxx 声明包。
社区的工作流大概是这样:
- 有人给一个没类型的 npm 包写了声明文件。
- 提交 Pull Request 到 DefinitelyTyped 仓库。
- DT 的维护者 review 后合并。
- 自动化工具(types-publisher)把声明文件打包发布到 npm 的
@types组织下。
所以你在 npm 上看到的 @types/react、@types/lodash、@types/node,它们的源头都是 DefinitelyTyped 仓库里的一个 PR。
安装 @types 包
安装方式就是普通的 npm 命令:
npm install --save-dev @types/lodash
装好之后,在 TypeScript 项目中就能直接用了:
import _ from "lodash";
_.padStart("Hello", 10, "-");
// TS 知道 padStart 接收三个参数,返回 string
NoteTS 7.0 的默认
types: []意味着安装@types/xxx之后,你还需要在tsconfig.json的types数组里显式声明它。不像 TS 6.x 及以前,装了就自动加载。
在 tsconfig.json 里启用:
{
"compilerOptions": {
"strict": true,
"types": ["node", "lodash"]
}
}
或者干脆不走 types 字段,而是通过 import 让 TS 自动解析——对于模块库的声明文件,import 语句本身就触发了类型加载。
@types 的版本号规则
@types 包的版本号遵循一条简单的规则:包名和版本号都与它对应的 npm 包保持一致。
以 @types/lodash 为例:
lodash 的版本:4.17.21
@types/lodash 的版本:4.17.21(或类似)
版本的 major.minor 要跟源包对齐,patch 版本可以独立变化(因为类型定义修改了不影响运行时)。举个例子:
| lodash 版本 | @types/lodash 版本 | 含义 |
|---|---|---|
| 4.17.21 | @types/lodash@4.17.21 | 类型定义了 lodash 4.17.x 的 API |
| 3.10.1 | @types/lodash@3.10.1 | 类型定义了 lodash 3.10.x 的 API |
如果 lodash 发布了 5.0.0,API 发生了破坏性变更,@types/lodash 也需要跟着升级到 5.0.x。版本号对齐是 DT 的类型正确性保证。
Tip如果你升级了一个 npm 包,记得同时检查
@types/xxx的版本是否匹配。版本不对齐最常见的表现是:某个方法突然类型不对了,或者新 API 完全没有类型提示。
如何查找类型定义
有两个方式:
方式一:npm 直接搜索
npm search @types/lodash
方式二:微软的类型搜索工具
访问 TypeScript 官方的类型搜索页面:
https://www.typescriptlang.org/dt/search
输入包名,它会告诉你:
- 是否有对应的
@types/xxx - 如果没有,这个包是否自带类型(bundled types)
- 最新版本和更新时间
另外,npm 包的页面右上角有一个 TypeScript 图标:蓝色六边形表示包自带类型,灰色六边形表示类型在 DT 上。
包自带的类型 vs @types
有些包把 .d.ts 文件直接打包在 npm 包里,不需要额外安装 @types。这类包的 package.json 会有 "types" 或 "typings" 字段:
{
"name": "some-library",
"version": "2.0.0",
"main": "./lib/index.js",
"types": "./lib/index.d.ts"
}
典型例子:date-fns、immer、zod。
如果一个包自带类型,你不需要也千万不要同时安装 @types/xxx——自带的类型和 DT 的类型可能冲突。
怎么区分?看 npm 包页面或 package.json 的 types 字段。有 types 的就是自带,没有的就去 DT 找。
贡献类型定义
假设你用的一个 npm 包既没有自带类型,DT 上也没有。你可以自己写一套声明然后提交给 DefinitelyTyped。
大致的贡献流程:
1. Fork DefinitelyTyped 仓库
https://github.com/DefinitelyTyped/DefinitelyTyped
2. 创建你的类型包目录
mkdir types/my-awesome-lib
3. 写声明文件
至少需要一个 index.d.ts:
// types/my-awesome-lib/index.d.ts
export function greet(name: string): string;
export function farewell(name: string): string;
4. 写 tsconfig.json
{
"compilerOptions": {
"module": "node16",
"lib": ["es6"],
"noImplicitAny": true,
"strictFunctionTypes": true,
"strictNullChecks": true
},
"files": ["index.d.ts"]
}
5. 写测试文件
DT 要求每个类型包都有对应的测试文件(my-awesome-lib-tests.ts):
import { greet, farewell } from "my-awesome-lib";
const msg: string = greet("TypeScript"); // 类型正确就应该编译通过
farewell("TypeScript");
测试文件不需要实际运行,只需要 tsc 编译通过即可——它验证的是你的类型定义在使用方式上的正确性。
6. 提交 Pull Request
Push 到你的 Fork 之后,在 GitHub 上提交 PR。DT 的 CI 会自动运行类型检查。维护者 review 通过后合并,你的类型包就会通过自动化流水线发布到 npm 的 @types/my-awesome-lib。
NoteDT 对声明文件的质量有一定要求:不能用
any偷懒、回调函数返回类型应该用void而不是any、不要用/// <reference path="..." />而用/// <reference types="..." />。建议提交前先读一遍 DT 的贡献指南。
DT 社区现状
DefinitelyTyped 目前有超过 8000 个类型包,活跃的维护者数百人。它是 TypeScript 生态中仅次于编译器本身的最大基础设施。
几个数字:
- 每周 npm 下载量超过 1 亿次的
@types/node - React 的类型定义在
@types/react(虽然 React 本身是 TS 写的,但类型定义量巨大,仍通过 DT 管理) - 几乎所有知名的 JS 工具链、数据库驱动、测试框架都在 DT 上有类型定义
社区维护方式的优势是覆盖面广、响应快。一个 npm 包发布一两周后,通常就有志愿者把类型定义提上去。它的劣势是质量参差——有些包的类型非常精确,有些只覆盖了主 API,边缘用法可能类型不全。
总结
@types/xxx是 DefinitelyTyped 社区维护的类型定义包。- 安装方式:
npm install --save-dev @types/xxx。 - 版本号跟源包对齐(major.minor 一致),patch 可独立。
- 包自带类型(有
"types"字段)就不要再装@types。 - 你可以给没类型的包写声明文件,通过 PR 提交到 DT 仓库回馈社区。
- TS 7.0 需要显式在
types字段声明使用的类型包。
下一章是这个系列的最后一章——不写 .d.ts 也能享受类型安全?没错,通过 JSDoc 注解和 tsc --declaration,你可以在纯 JS 项目里渐进地引入类型。