《彻底搞懂 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 的导入导出方式,既能保持代码清晰,又能享受类型系统带来的优势。

相关推荐
難釋懷3 小时前
TypeScript-webpack
javascript·webpack·typescript
摸鱼仙人~2 天前
如何创建基于 TypeScript 的 React 项目
javascript·react.js·typescript
一生躺平的仔2 天前
TypeScript入门(九)装饰器:TypeScript的"元编程超能力"
typescript
MiyueFE2 天前
让我害怕的 TypeScript 类型 — — 直到我学会了这 3 条规则
前端·typescript
前端拿破轮2 天前
😭😭😭看到这个快乐数10s,我就知道快乐不属于我了🤪
算法·leetcode·typescript
前端_ID林2 天前
每个开发人员都应该知道的 TypeScript 技巧
typescript
奋飛2 天前
TypeScript系列:第六篇 - 编写高质量的TS类型
javascript·typescript·ts·declare·.d.ts
BillKu12 天前
Vue3 + TypeScript + xlsx 导入excel文件追踪数据流转详细记录(从原文件到目标数据)
前端·javascript·typescript
小Lu的开源日常12 天前
Drizzle vs Prisma:现代 TypeScript ORM 的深度对比
数据库·typescript·前端框架
Shixaik13 天前
配置@为src
typescript·前端框架