在 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 }; // 编译错误:重复标识符
.d.ts 文件中如果没有 import 或 export,该文件就是全局声明文件,所有类型可直接在任何地方使用。export {},将文件变为 ES 模块,类型不再污染全局。export {} 将文件变为模块,再在 declare global { } 块中声明。export 导出类型,使用时按需 import,避免全局类型污染。// 正确实践:给 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