← 返回导航

给第三方库补类型声明

进阶技巧 · 中级

场景说明

在实际项目中,经常遇到没有类型声明的 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 类型安全

关键点解析

使用示例与类型推导


      
// 实战:给 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');
在 Playground 中尝试