APE
文章标签归档关于

© 2026 APE.PUB

2026-08-12· 6 分钟

typescript-@types

typescript

TypeScript 类型与 @types 包机制详解

1. @types/* 是什么

@types/* 是 TypeScript 的类型声明包(大多来自 DefinitelyTyped 社区维护),用于给不内置类型声明的 JS 库提供编译期类型。它只影响编译期类型检查,不参与运行时逻辑。

对应关系:命名约定

@types/react  →  npm 包 react
@types/node   →  npm 包 node
@types/lodash →  npm 包 lodash
  • 普通包:@types/<包名>
  • scoped 包(如 @scope/pkg):对应 @types/scope__pkg,即 / 替换为 __

类型与运行时分离

| 层面 | 来源 | 用途 |

|------|------|------|

| 类型声明 | @types/react | 编译期类型检查 |

| 实际代码 | react | 运行时执行 |

所以 @types/* 通常放在 devDependencies 中,运行时完全不依赖它。

2. TS 编译器如何"识别" @types 包

关键结论:不是靠字符串命名匹配,而是靠目录查找机制。

TS 的模块解析顺序(以 import React from 'react' 为例):

1. 查 node_modules/react 包本身:

  • 看 package.json 的 types / typings 字段
  • 或包内的 .d.ts 文件(如 index.d.ts)

2. 若包内没有类型声明,则 fallback 到 node_modules/@types/react/ 查找同名子目录

自动包含机制

TS 默认会扫描 node_modules/@types 下的所有包并自动加入全局类型环境(对应 tsconfig 的 typeRoots 选项,默认值为 ./node_modules/@types)。这也是为什么装好 @types/* 后通常无需手动 import 就能获得类型提示。

3. @types-myself 这类包能自动对应吗?

不能。 设想一个叫 @types-myself 的普通包内含 react 类型:

  • 它位于 node_modules/@types-myself,不是 node_modules/@types/ 目录,是普通包
  • import React from 'react' 时 TS 根本不会去查它
  • 会直接报错:Could not find a declaration file for module 'react'
  • 即使显式 import type ... from '@types-myself/react',该类型不会附着到 react 模块上

要让自定义位置的类型生效,只能显式映射:

{
  "compilerOptions": {
    "paths": {
      "react": ["@types-myself/react"]
    }
  }
}

或使用 triple-slash 引用:///

总结:唯一"命名即自动生效"的机制

  • node_modules/@types/<名字> 是唯一约定即自动生效的位置
  • 其他任何路径都必须通过 paths、typeRoots 或 triple-slash 显式声明

4. @types/ 目录的特殊性

node_modules/@types/ 目录确实是特殊的,特殊之处在于:

1. 目录路径特殊:TS 默认自动扫描它(typeRoots 默认指向它)

2. 命名即映射:子目录名 = 模块名(@types/react 目录名 react 决定它服务哪个模块)

3. 唯一性:该目录名不能随意改(typeRoots 指向任何目录,但那个目录不再具有"命名即生效"的魔力,需显式配置)

所以"特殊"= 目录路径特殊,不是命名规则特殊。

5. 文件/文件夹对同名类型的隔离

隔离主体是"文件 = 模块",不是文件夹

  • 每个文件(模块)是独立作用域:内部声明的类型/变量外部不可见,除非 export
  • 两个不同文件里都写 export interface Foo,互不干扰
  • 文件夹不产生任何作用域:目录结构对 TS 没有任何语义
  • src/a/x.ts 与 src/b/x.ts 不因路径不同而隔离
  • 真正隔离它们的是"各自是独立文件(模块)"

隔离不绝对的例外

1. 全局脚本文件:没有 import/export 的文件是全局脚本,声明进入全局作用域,同名会冲突

2. 声明合并(declaration merging):同名 interface、namespace 会自动合并,穿透文件边界

3. declare global:显式往全局塞类型

结论

同名的类型/变量默认被文件隔离(靠模块机制,不靠文件夹),前提是文件是模块;文件夹本身不参与隔离。

6. 核心要点速记

| 问题 | 答案 |

|------|------|

| @types/react 如何对应 react? | 目录查找:TS 模块解析 fallback 到 node_modules/@types/react |

| 是命名识别吗? | 不是,是 node_modules/@types/ 目录 + 子目录名匹配 |

| @types-myself 能自动生效吗? | 不能,需要 paths 映射或 triple-slash 显式声明 |

| @types/ 目录特殊吗? | 特殊,特殊在"目录路径"而非命名规则 |

| 同名类型如何隔离? | 靠文件(模块)作用域,不靠文件夹 |

← 返回首页