← 返回导航

全局类型声明污染

踩坑记录

场景说明

在 TypeScript 项目中,.d.ts 文件如果没有 import/export 语句,就会被视为全局声明文件(ambient module),其中的类型会污染全局命名空间。在组件开发中,经常因为这个机制导致全局类型冲突、命名重复等问题。

完整代码


      
// 全局类型声明污染的常见问题与解决方案

// ==================== 问题 ====================

// 文件:components/Button/types.d.ts
// 没有 import/export,这是全局声明文件!
interface Size {           // 污染全局!
  width: number;
  height: number;
}

type Theme = 'light' | 'dark';  // 污染全局!

// 文件:components/Modal/types.d.ts
// 也没有 import/export,Size 和 Theme 冲突!
interface Size {           // 错误:重复标识符 "Size"
  width: string;           // 与上面的 Size 合并,但类型冲突!
  height: string;
}

// ==================== 解决方案一:加 export {} ====================

// 文件:components/Button/types.d.ts
export {};  // 将文件变为模块,不再污染全局

interface ButtonSize {
  width: number;
  height: number;
}

type ButtonTheme = 'light' | 'dark';

// ==================== 解决方案二:declare global ====================

// 文件:types/global.d.ts
export {};  // 先变成模块

declare global {      // 在 global 中声明,这才是真正的全局扩展
  interface Window {
    __APP_VERSION__: string;  // 给 window 挂载全局变量
    gtag: function;
  }

  namespace App {
    interface Config {
      env: 'dev' | 'prod';
      apiBase: string;
    }
  }
}

// ==================== 解决方案三:模块化导出 ====================

// 文件:components/Button/types.ts
export interface ButtonSize {
  width: number;
  height: number;
}

export type ButtonTheme = 'light' | 'dark';

// 使用时按需导入
// import { ButtonSize, ButtonTheme } from './Button/types';

// ==================== 实际踩坑场景 ====================

// 坑:在 .d.ts 中声明了通用的类型名导致冲突
// types.d.ts(忘了加 export {})
type Data = { id: number };  // 全局污染

// 另一个 .d.ts(也忘了加 export {})
type Data = { id: string };  // 编译错误:重复标识符

关键点解析

使用示例与类型推导


      
// 正确实践:给 Vue 原型挂载属性声明类型

// 文件:types/vue-global.d.ts
export {};  // 关键:先变成模块

declare module 'vue' {
  export interface ComponentCustomProperties {
    $http: typeof axios;
    $bus: EventBus;
    $filters: {
      formatDate: (date: Date) => string;
      formatMoney: (amount: number) => string;
    };
  }
}

// 正确实践:给 window 挂载全局变量

// 文件:types/window.d.ts
export {};

declare global {
  interface Window {
    __INITIAL_STATE__: Record<string, unknown>;
    APP_CONFIG: {
      apiBaseUrl: string;
      version: string;
    };
  }
}

// 使用时类型安全
// console.log(window.APP_CONFIG.apiBaseUrl);  // string
在 Playground 中尝试