《彻底搞懂 TypeScript 类型导入:export、declare 与全局类型的正确打开方式》

前言

在 TypeScript 开发中,我们经常需要在不同文件间共享类型定义。但你是否遇到过这些困惑:

  • 为什么有些类型可以直接使用而不用导入?
  • 什么时候用 export,什么时候用 declare
  • 为什么同样的类型声明方式在不同文件中表现不一致?

本文将彻底解析 TypeScript 的类型作用域规则,帮你建立清晰的心智模型。

一、TypeScript 的三种类型作用域

1. 模块作用域(需要 import/export)

​特征​​:

  • 文件中有 importexport 语句(包括 export {}

  • 必须显式导入才能使用其他文件中的类型

    // types.ts export interface User { // 必须显式导出 name: string; }

    // app.ts import { User } from './types'; // 必须显式导入 const user: User = { name: 'Jack' };

2. 全局作用域(自动生效)

​特征​​:

  • .d.ts 声明文件中

  • 没有 importexport 语句

  • 项目内任何地方可直接使用

    // 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:为什么我的类型突然不可用了?

​检查步骤​​:

  1. 查看声明文件是否意外添加了 import/export
  2. 检查 tsconfig.jsoninclude/exclude 配置
  3. 确认没有同名局部类型覆盖

问题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 的类型作用域规则后,你会发现:

  1. 大多数情况下应该使用显式的模块化方案
  2. 全局类型声明要谨慎使用
  3. declare 主要用于类型声明文件

​最终建议​​:在新项目中坚持使用 ES Modules 的导入导出方式,既能保持代码清晰,又能享受类型系统带来的优势。

相关推荐
Raink老师10 小时前
6. TypeScript 函数
前端·javascript·typescript
A-wliang15 小时前
从零开始搭建现代化 Monorepo 开发模板:TypeScript + Rollup + Jest + 持续集成完整指南
ubuntu·ci/cd·typescript
很甜的西瓜1 天前
typescript软渲染实现类似canvas的2d矢量图形引擎
前端·javascript·typescript·图形渲染·canvas
Mr...Gan1 天前
TypeScript
开发语言·javascript·typescript
0x00071 天前
HarmonyOS 应用开发学习记录 - 从Windows开发者视角看鸿蒙开发
华为·typescript·harmonyos·鸿蒙系统
这里是阿栗1 天前
TS进阶!深入探索 TypeScript 工具类型:内置神器与高阶扩展
前端·typescript
non_hana2 天前
一些 linter & formatter 配置最佳实践
typescript·node.js·eslint
FlyingBird~2 天前
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
java·javascript·typescript
我不吃饼干2 天前
从 Vue3 源码中了解你所不知道的 never
前端·typescript