FumadocsZDecode
Typescript

拓展声明

TypeScript 的拓展声明(Declaration Augmentation)允许你在不修改原始代码的情况下,为现有模块、全局对象或第三方库添加类型信息。你在使用第三方库时给它补充缺失的类型、给 window 对象加属性、或者扩展框架的内置类型,都会用到这个机制。


模块扩充(Module Augmentation)

模块扩充用于给已有模块添加类型,而不重写它。用 declare module '模块名' 包裹你要添加的声明。

场景:给第三方库补充缺失的导出

// types/awesome-library.d.ts
declare module 'awesomeLibrary' {
  export function someFunction(param: string): number

  export class SomeClass {
    constructor(value: string)
    method(): void
  }
}

场景:给现有模块追加导出

如果模块已经有类型声明,你可以在自己的文件里追加内容,TypeScript 会自动合并:

// 原始模块 express 已经有类型
// 现在给 Request 对象加一个自定义属性
declare module 'express' {
  interface Request {
    user?: {
      id: string
      role: 'admin' | 'guest'
    }
  }
}

// 使用时 TypeScript 能识别 req.user
import { Request, Response } from 'express'

function handler(req: Request, res: Response) {
  if (req.user?.role === 'admin') {
    res.json({ ok: true })
  }
}

模块扩充文件必须是一个模块文件(包含至少一个 importexport),否则 TypeScript 会把它当作全局声明文件处理:

// ✅ 正确:文件中有 export {},TypeScript 将其识别为模块
export {}

declare module 'some-lib' {
  export function helper(): void
}

全局扩充(Global Augmentation)

全局扩充用于给全局作用域添加类型,比如给 windowglobalThis 或全局函数补充类型。

declare global 包裹声明:

// types/global.d.ts
export {}  // 让 TypeScript 把这个文件视为模块

declare global {
  // 给 window 对象追加属性
  interface Window {
    analytics: {
      track(event: string, props?: Record<string, unknown>): void
    }
  }

  // 声明全局变量
  const __APP_VERSION__: string

  // 声明全局函数
  function formatDate(date: Date, locale?: string): string
}

声明后可以直接使用:

window.analytics.track('page_view', { path: '/home' })

console.log(__APP_VERSION__) // ✅ TypeScript 知道这是 string

const label = formatDate(new Date()) // ✅

接口合并(Interface Merging)

TypeScript 会自动把同名接口合并为一个,这是 TypeScript 独有的能力(type 别名不支持合并)。

interface User {
  id: string
  name: string
}

interface User {
  email: string
  createdAt: Date
}

// 合并后等价于:
// interface User {
//   id: string
//   name: string
//   email: string
//   createdAt: Date
// }

const user: User = {
  id: '1',
  name: 'Alice',
  email: 'alice@example.com',
  createdAt: new Date(),
}

接口合并常用于扩展第三方库的类型定义:

// 扩展 Vue 的全局属性类型
declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $http: typeof fetch
    $router: Router
  }
}

命名空间合并(Namespace Merging)

命名空间也支持合并,可以将声明分散在多个文件或多个位置:

namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean
  }
}

namespace Validation {
  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return /^[A-Za-z]+$/.test(s)
    }
  }
}

// 合并后两者都可以访问
const validator: Validation.StringValidator = new Validation.LettersOnlyValidator()

命名空间还可以与函数、类合并,用于给函数或类附加属性:

function createLogger(name: string): Logger {
  return new Logger(name)
}

// 给函数追加属性
namespace createLogger {
  export const defaultLevel = 'info'
  export function setGlobalLevel(level: string): void {
    // ...
  }
}

// 使用函数本身
const logger = createLogger('app')

// 使用挂载在函数上的属性
createLogger.setGlobalLevel('debug')
console.log(createLogger.defaultLevel) // 'info'

declare 关键字

declare 告诉 TypeScript "这个值存在,但不在这个文件里定义"。它只产生类型信息,不生成任何 JavaScript 代码。

// 声明一个全局变量(由构建工具注入)
declare const __DEV__: boolean

// 声明一个全局函数
declare function require(module: string): unknown

// 声明一个外部类
declare class EventEmitter {
  on(event: string, listener: (...args: unknown[]) => void): this
  emit(event: string, ...args: unknown[]): boolean
}

// 声明一个外部枚举(值由运行时决定)
declare enum Direction {
  Up,
  Down,
  Left,
  Right,
}

declare 常见位置:

位置作用
.d.ts 文件顶层为 JS 库提供类型声明
declare module '...' 块内模块扩充
declare global 块内全局扩充
declare namespace 块内命名空间扩充

.d.ts 声明文件

声明文件(.d.ts)专门用于存放类型声明,不包含任何实现代码。TypeScript 编译器会自动加载它们。

文件结构示例:

src/
  index.ts
types/
  global.d.ts       ← 全局类型扩充
  third-party.d.ts  ← 第三方库类型补充

tsconfig.json 中配置让 TypeScript 找到这些文件:

{
  "compilerOptions": {
    "typeRoots": ["./types", "./node_modules/@types"]
  }
}

或者用 include 字段包含整个目录:

{
  "include": ["src/**/*", "types/**/*"]
}

常见使用场景

1. 给 window 追加第三方脚本注入的属性

// types/global.d.ts
export {}

declare global {
  interface Window {
    gtag: (command: string, ...args: unknown[]) => void
    dataLayer: unknown[]
  }
}

// 使用时不报错
window.gtag('event', 'click', { button: 'submit' })

2. 扩展 Express 的 Request 类型

// types/express.d.ts
import { User } from '../src/models/user'

declare module 'express-serve-static-core' {
  interface Request {
    currentUser?: User
  }
}

3. 给 CSS Modules 提供类型

// types/css-modules.d.ts
declare module '*.module.css' {
  const styles: Record<string, string>
  export default styles
}

declare module '*.module.scss' {
  const styles: Record<string, string>
  export default styles
}

使用后导入 CSS Modules 不再报错:

import styles from './Button.module.css'
// styles.container, styles.primary 等都有类型

4. 给构建工具注入的环境变量补充类型

// types/env.d.ts
declare global {
  const __APP_VERSION__: string
  const __BUILD_TIME__: string
  const __IS_DEV__: boolean
}

export {}

On this page