拓展声明
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 })
}
}模块扩充文件必须是一个模块文件(包含至少一个 import 或 export),否则 TypeScript 会把它当作全局声明文件处理:
// ✅ 正确:文件中有 export {},TypeScript 将其识别为模块
export {}
declare module 'some-lib' {
export function helper(): void
}全局扩充(Global Augmentation)
全局扩充用于给全局作用域添加类型,比如给 window、globalThis 或全局函数补充类型。
用 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 {}