前言
在 TypeScript 开发中,我们经常需要在不同文件间共享类型定义。但你是否遇到过这些困惑:
- 为什么有些类型可以直接使用而不用导入?
- 什么时候用
export
,什么时候用declare
? - 为什么同样的类型声明方式在不同文件中表现不一致?
本文将彻底解析 TypeScript 的类型作用域规则,帮你建立清晰的心智模型。
一、TypeScript 的三种类型作用域
1. 模块作用域(需要 import/export)
特征:
-
文件中有
import
或export
语句(包括export {}
) -
必须显式导入才能使用其他文件中的类型
// types.ts export interface User { // 必须显式导出 name: string; }
// app.ts import { User } from './types'; // 必须显式导入 const user: User = { name: 'Jack' };
2. 全局作用域(自动生效)
特征:
-
在
.d.ts
声明文件中 -
没有
import
或export
语句 -
项目内任何地方可直接使用
// global.d.ts declare namespace GlobalTypes { interface Book { // 自动全局可用 title: string; } }
// app.ts const book: GlobalTypes.Book = { title: 'TS Guide' }; // 无需导入
3. 混合作用域(谨慎使用)
typescript
// special.d.ts
import { SomeType } from 'some-module'; // 有 import 语句
declare global { // 显式声明全局类型
interface Window {
myLib: any;
}
}
二、export 与 declare 的核心区别
关键字
适用场景
是否需要实现
典型用途
export
模块中的类型/值
可选
共享业务逻辑类型
declare
声明已存在的类型/值
不需要
为JS库添加类型声明
经典案例:为第三方库扩展类型
typescript
// vue-augmentation.d.ts
import { ComponentCustomProperties } from 'vue';
declare module 'vue' { // 声明扩展
interface ComponentCustomProperties {
$myGlobalMethod: () => void;
}
}
三、实战中的最佳实践
1. 现代项目推荐方案
方案:统一使用模块化(始终用 import/export)
typescript
// types/user.ts
export interface User { ... }
// utils/helper.ts
export type HelperOptions = { ... }
// app.ts
import type { User } from './types/user';
import { HelperOptions } from './utils/helper';
优点:
- 明确的依赖关系
- 更好的代码可维护性
- 兼容 tree-shaking
2. 需要全局类型的场景
场景:项目全局使用的基础类型
typescript
// types/global.d.ts
declare type Recordable<T = any> = Record<string, T>;
declare type Nullable<T> = T | null;
// 任何文件可直接使用
const data: Recordable = { ... };
3. 声明合并的特殊用法
typescript
// types/router.d.ts
import { RouteRecordRaw } from 'vue-router';
declare module 'vue-router' { // 声明合并
interface RouteMeta {
requiresAuth: boolean;
hidden?: boolean;
}
}
四、常见问题排查指南
问题1:为什么我的类型突然不可用了?
检查步骤:
- 查看声明文件是否意外添加了
import/export
- 检查
tsconfig.json
的include/exclude
配置 - 确认没有同名局部类型覆盖
问题2:如何让自定义声明文件生效?
解决方案:
json
// tsconfig.json
{
"compilerOptions": {
"typeRoots": ["./typings", "./node_modules/@types"]
}
}
五、TypeScript 4.0+ 的新特性
1. 类型导入语法
typescript
import type { SomeType } from 'module'; // 明确表示只导入类型
2. 内联类型导入
javascript
// 直接在使用处导入
function greet(user: import("./types").User) { ... }
结语
理解 TypeScript 的类型作用域规则后,你会发现:
- 大多数情况下应该使用显式的模块化方案
- 全局类型声明要谨慎使用
- declare 主要用于类型声明文件
最终建议:在新项目中坚持使用 ES Modules 的导入导出方式,既能保持代码清晰,又能享受类型系统带来的优势。