在实际项目中,经常遇到没有类型声明的 npm 包、需要扩展的第三方库类型、或者要给 window 等全局对象挂载属性。通过 declare module 可以给这些"无类型"的代码补充类型声明,让项目享受完整的类型安全。
// 给第三方库补类型声明的三种方式
// ==================== 方式一:declare module 扩展现有模块 ====================
// 场景:给一个没有 @types 的 npm 包补类型
// 文件:types/legacy-lib.d.ts
declare module 'legacy-lib' {
export function init(options: { appId: string; debug?: boolean }): void;
export function track(event: string, data?: Record<string, unknown>): void;
export function getVersion(): string;
}
// 场景:扩展已有模块的类型(如给 axios 添加自定义配置)
// 文件:types/axios-extend.d.ts
import axios from 'axios';
declare module 'axios' {
export interface AxiosRequestConfig {
// 扩展配置:添加自定义字段
showLoading?: boolean;
retryCount?: number;
cacheKey?: string;
}
}
// ==================== 方式二:给 .vue / .css / .scss 等非 JS 文件声明类型 ====================
// 文件:types/shims.d.ts
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}
declare module '*.module.css' {
const classes: Record<string, string>;
export default classes;
}
declare module '*.png' {
const src: string;
export default src;
}
// ==================== 方式三:给 window / global 挂载全局变量 ====================
// 文件:types/global.d.ts
export {}; // 关键:将文件变为模块,避免污染全局
declare global {
interface Window {
__INITIAL_STATE__: Record<string, unknown>;
APP_CONFIG: {
apiBaseUrl: string;
version: string;
env: 'dev' | 'staging' | 'prod';
};
}
interface String {
// 扩展 String 原型(慎用!)
toCamelCase(): string;
}
}
// 使用:window.APP_CONFIG.apiBaseUrl 类型安全
'xxx/sub'),可以声明子路径的类型。declare module 'axios' 可以给已有的模块添加新的类型声明,不会覆盖原有类型,而是合并。declare module '*.vue' 让 TS 理解非 JS/TS 文件的导入,避免 Cannot find module 错误。export {} 将文件变为模块。types/ 或 src/types/ 目录下,确保 tsconfig.json 的 include 包含这些文件。// 实战:给 Vue 原型挂载属性声明类型
// 文件:types/vue.d.ts
export {};
declare module 'vue' {
export interface ComponentCustomProperties {
$http: typeof axios;
$message: {
success: (msg: string) => void;
error: (msg: string) => void;
warning: (msg: string) => void;
};
$filters: {
formatDate: (date: string | Date, fmt?: string) => string;
formatMoney: (amount: number, decimals?: number) => string;
};
}
}
// 组件中使用,类型安全
// this.$message.success('保存成功');
// this.$filters.formatDate(new Date(), 'YYYY-MM-DD');