TypeScript 全局类型声明:declare关键字的深度解析与实战
引言:declare关键字的本质与哲学
在 TypeScript 中,declare 关键字是类型系统与现实世界的桥梁。它的核心作用是告诉 TypeScript 编译器:"相信我,这个实体在运行时存在,你不需要检查它的实现,但请根据我描述的类型来验证使用它的代码。"
"declare 不是逃避类型检查,而是建立信任边界。" - TypeScript 设计哲学
第一部分:declare 的底层原理与类型系统架构
1.1 TypeScript 编译器如何处理 declare
当 TypeScript 编译器遇到 declare 关键字时,会进入特殊处理模式:
typescript
// 源代码分析
declare const VERSION: string;
// 编译器内部处理过程
class DeclareProcessor {
processDeclareStatement(node: DeclareNode) {
// 阶段1:语法解析
const declaration = this.parseDeclaration(node);
// 阶段2:符号表注册
this.registerSymbol(declaration);
// 阶段3:类型信息附加
this.attachTypeInfo(declaration);
// 关键:跳过实现检查!
// 编译器相信声明是真实的,不生成任何运行时代码
}
registerSymbol(declaration) {
// 在符号表中标记为"已声明"
this.symbolTable.set(declaration.name, {
kind: 'declare',
type: declaration.type,
// 特殊标志:只在类型空间存在
existsIn: ['type-space'],
// 不会生成到输出中
emit: false
});
}
}
类型系统的双重世界:
typescript
// 类型空间(Type Space) - 编译时
declare interface User {
id: string;
name: string;
}
// 值空间(Value Space) - 运行时
const user: User = {
id: '123',
name: 'Alice'
};
// declare 创建的实体只存在于类型空间
// 它们不会出现在 JavaScript 输出中
1.2 declare 与 JavaScript 的运行时类型差异
| 特性 | declare 声明 |
普通 TypeScript 声明 |
|---|---|---|
| 编译输出 | 不生成任何代码 | 生成对应的 JavaScript 代码 |
| 类型检查 | 完全信任声明 | 检查实现是否符合类型 |
| 作用域 | 可在 .d.ts 文件中使用 | 可在 .ts 文件中使用 |
| 运行时影响 | 无 | 直接影响运行时代码 |
| 模块系统 | 可描述外部模块 | 描述内部实现 |
第二部分:declare 的基本用法详解
2.1 声明全局变量
typescript
// 声明全局常量
declare const __DEV__: boolean;
declare const __VERSION__: string;
declare const __BUILD_TIME__: number;
// 声明全局变量(可重新赋值)
declare let currentUserId: string | null;
// 声明全局函数
declare function trackEvent(event: string, data?: Record<string, any>): void;
// 声明全局类
declare class ExternalWidget {
constructor(element: HTMLElement);
render(data: any): void;
destroy(): void;
}
// 实际使用
if (__DEV__) {
console.log(`Version: ${__VERSION__}, Built at: ${new Date(__BUILD_TIME__)}`);
}
currentUserId = 'user-123';
trackEvent('page_view', { path: window.location.pathname });
const widget = new ExternalWidget(document.getElementById('widget'));
widget.render({ title: 'Hello' });
2.2 声明命名空间(全局模块)
typescript
// 声明全局命名空间
declare namespace MyApp {
// 嵌套接口
export interface Config {
apiUrl: string;
timeout: number;
features: {
analytics: boolean;
darkMode: boolean;
};
}
// 嵌套函数
export function initialize(config: Partial<Config>): void;
// 嵌套类
export class Logger {
static info(message: string, meta?: any): void;
static error(error: Error | string): void;
static warn(message: string): void;
}
// 嵌套枚举
export enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3
}
// 嵌套常量
export const DEFAULT_CONFIG: Config;
}
// 使用全局命名空间
MyApp.initialize({
apiUrl: 'https://api.example.com',
features: { analytics: true, darkMode: false }
});
MyApp.Logger.info('App initialized');
2.3 声明全局类型别名和接口
typescript
// 全局类型别名
declare type UUID = string & { readonly __brand: 'UUID' };
declare type Email = string & { readonly __brand: 'Email' };
declare type RGB = `rgb(${number}, ${number}, ${number})`;
declare type HEX = `#${string}`;
// 全局接口(自动合并到全局作用域)
declare interface GlobalEventMap {
'user-login': { userId: string; timestamp: number };
'user-logout': { userId: string };
'data-loaded': { data: any; source: string };
}
// 全局工具类型
declare type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
declare type Nullable<T> = T | null | undefined;
// 使用全局类型
const userId: UUID = '123e4567-e89b-12d3-a456-426614174000' as UUID;
const userEmail: Email = 'user@example.com' as Email;
function emitEvent<T extends keyof GlobalEventMap>(
event: T,
data: GlobalEventMap[T]
): void {
// 事件派发逻辑
}
第三部分:declare 的高级模式与技巧
3.1 条件声明与环境检测
typescript
// 根据环境变量声明不同的全局变量
declare const process: {
env: {
NODE_ENV: 'development' | 'production' | 'test';
[key: string]: string | undefined;
};
};
// 浏览器环境专属声明
declare namespace browser {
// 只有浏览器环境才有的API
export interface Window {
webkitRequestAnimationFrame?: (callback: FrameRequestCallback) => number;
mozRequestAnimationFrame?: (callback: FrameRequestCallback) => number;
msRequestAnimationFrame?: (callback: FrameRequestCallback) => number;
}
}
// Node.js 环境专属声明
declare namespace node {
export interface Process {
pid: number;
platform: string;
memoryUsage(): NodeJS.MemoryUsage;
}
export interface Buffer {
// Node.js Buffer 特有的方法
swap16(): Buffer;
swap32(): Buffer;
swap64(): Buffer;
}
}
// 跨平台通用类型
type Platform = 'browser' | 'node' | 'deno' | 'unknown';
declare function getPlatform(): Platform;
// 条件类型声明
declare type PlatformSpecific<T> =
Platform extends 'browser' ? T & { browserOnly: true } :
Platform extends 'node' ? T & { nodeOnly: true } :
T;
3.2 声明合并与模块增强
typescript
// 扩展现有全局接口
interface Window {
// 添加自定义属性
myCustomProperty: string;
// 添加自定义方法
showNotification(message: string, type?: 'info' | 'warning' | 'error'): void;
// 添加可能由浏览器扩展注入的API
chrome?: {
runtime: {
sendMessage(message: any): void;
onMessage: {
addListener(callback: (message: any) => void): void;
};
};
};
}
// 扩展 Document 接口
interface Document {
// 添加实验性API
exitPointerLock(): void;
mozExitPointerLock?(): void;
webkitExitPointerLock?(): void;
// 自定义方法
getElementById<T extends HTMLElement = HTMLElement>(id: string): T | null;
}
// 扩展 Array 原型(谨慎使用!)
interface Array<T> {
// 添加自定义方法
findLast(predicate: (value: T, index: number, obj: T[]) => boolean): T | undefined;
findLastIndex(predicate: (value: T, index: number, obj: T[]) => boolean): number;
// 添加工具方法
groupBy<K extends string | number | symbol>(
keySelector: (item: T, index: number) => K
): Record<K, T[]>;
}
// 扩展 String 原型
interface String {
// 添加格式化方法
format(...args: any[]): string;
// 添加验证方法
isEmail(): boolean;
isPhoneNumber(): boolean;
// 添加转换方法
toCamelCase(): string;
toKebabCase(): string;
toSnakeCase(): string;
}
3.3 高级类型运算与 declare 的组合
typescript
// 使用模板字面量类型声明
declare type RouteParam<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? { [K in Param | keyof RouteParam<`/${Rest}`>]: string }
: T extends `${string}:${infer Param}`
? { [K in Param]: string }
: {};
declare function createRoute<T extends string>(
path: T
): {
path: T;
match(url: string): RouteParam<T> | null;
build(params: RouteParam<T>): string;
};
// 使用条件类型声明泛型工具
declare type ResolvedPromise<T> =
T extends Promise<infer U> ? U : T;
declare type AsyncFunctionReturn<T> =
T extends (...args: any[]) => Promise<infer R> ? R : never;
// 声明高级映射类型
declare type MappedConfig<T extends Record<string, any>> = {
[K in keyof T as `config_${string & K}`]: {
value: T[K];
description: string;
editable: boolean;
};
};
// 使用 infer 进行模式匹配声明
declare type ExtractEventType<T> =
T extends { type: infer Type } ? Type : never;
declare type ExtractEventData<T, K> =
T extends { type: K; data: infer Data } ? Data : never;
第四部分:为第三方库创建全局声明
4.1 为无类型库创建全局声明
typescript
// 假设有一个名为 legacy-chart 的全局库,通过 <script> 标签引入
// 声明全局变量
declare const LegacyChart: {
// 构造函数
new (element: HTMLElement, options?: ChartOptions): ChartInstance;
// 静态方法
registerTheme(name: string, theme: ThemeOptions): void;
registerPlugin(name: string, plugin: Plugin): void;
// 静态属性
version: string;
defaultTheme: string;
};
// 声明相关类型
declare interface ChartOptions {
type?: 'line' | 'bar' | 'pie' | 'scatter';
width?: number;
height?: number;
title?: string;
data?: DataPoint[];
colors?: string[];
animation?: boolean;
}
declare interface DataPoint {
x: number | string;
y: number;
label?: string;
}
declare interface ThemeOptions {
backgroundColor?: string;
textColor?: string;
gridColor?: string;
fontFamily?: string;
}
declare interface Plugin {
beforeRender?(chart: ChartInstance): void;
afterRender?(chart: ChartInstance): void;
beforeUpdate?(chart: ChartInstance, data: DataPoint[]): void;
afterUpdate?(chart: ChartInstance, data: DataPoint[]): void;
}
declare interface ChartInstance {
// 实例方法
render(): void;
update(data: DataPoint[]): void;
resize(width: number, height: number): void;
destroy(): void;
// 实例属性
readonly element: HTMLElement;
readonly options: ChartOptions;
readonly isRendered: boolean;
}
// 使用示例
const chart = new LegacyChart(document.getElementById('chart'), {
type: 'line',
width: 800,
height: 600,
title: 'Sales Data'
});
chart.render();
4.2 为 jQuery 插件创建声明
typescript
// 扩展 jQuery 的接口
interface JQuery {
// 日期选择器插件
datepicker(options?: DatepickerOptions): JQuery;
datepicker(method: 'getDate'): Date | null;
datepicker(method: 'setDate', date: Date | string): JQuery;
datepicker(method: 'destroy'): JQuery;
// 工具提示插件
tooltip(options?: TooltipOptions): JQuery;
tooltip(method: 'show'): JQuery;
tooltip(method: 'hide'): JQuery;
tooltip(method: 'toggle'): JQuery;
// 模态框插件
modal(options?: ModalOptions): JQuery;
modal(method: 'show'): JQuery;
modal(method: 'hide'): JQuery;
modal(method: 'toggle'): JQuery;
// 通用插件模式
[pluginName: string]: any; // 为未知插件提供回退
}
// 插件选项接口
interface DatepickerOptions {
dateFormat?: string;
minDate?: Date | string;
maxDate?: Date | string;
showAnim?: string;
showButtonPanel?: boolean;
changeMonth?: boolean;
changeYear?: boolean;
yearRange?: string;
}
interface TooltipOptions {
content?: string | ((response: any) => string);
position?: {
my?: string;
at?: string;
collision?: string;
};
show?: {
effect?: string;
delay?: number;
};
hide?: {
effect?: string;
delay?: number;
};
track?: boolean;
}
interface ModalOptions {
backdrop?: boolean | 'static';
keyboard?: boolean;
show?: boolean;
focus?: boolean;
}
// 使用示例
$('#datepicker').datepicker({
dateFormat: 'yy-mm-dd',
minDate: new Date(),
changeMonth: true,
changeYear: true
});
$('#tooltip').tooltip({
content: '这是一个提示信息',
position: { my: 'left center', at: 'right center' }
});
第五部分:模块系统与 declare 的深度整合
5.1 CommonJS 模块声明
typescript
// 声明 CommonJS 模块
declare module 'legacy-commonjs-module' {
// 导出单个函数
function mainFunction(param: string): number;
// 导出对象
const utils: {
helper1(): void;
helper2(): void;
};
// 导出类
class LegacyClass {
constructor(value: number);
doSomething(): void;
}
// CommonJS 导出语法
export = {
mainFunction,
utils,
LegacyClass
};
}
// 使用方式1:require 语法
import legacyModule = require('legacy-commonjs-module');
legacyModule.mainFunction('test');
// 使用方式2:ES6 import(需要开启 allowSyntheticDefaultImports)
import * as legacyModule from 'legacy-commonjs-module';
legacyModule.mainFunction('test');
5.2 UMD 模块声明
typescript
// 声明 UMD 模块(同时支持 CommonJS、AMD 和全局变量)
declare module 'umd-library' {
// 模块导出
export interface UmdConfig {
debug: boolean;
timeout: number;
}
export function initialize(config: Partial<UmdConfig>): void;
export class UmdClass {
constructor();
method(): void;
}
// 默认导出(UMD 模块通常有默认导出)
export default {
initialize,
UmdClass
};
// 全局变量导出(当通过 <script> 标签引入时)
export as namespace umdLibrary;
}
// 使用方式1:模块导入
import umdLibrary from 'umd-library';
umdLibrary.initialize({ debug: true });
// 使用方式2:全局变量(当通过 <script> 标签引入时)
// umdLibrary 会作为全局变量存在
umdLibrary.initialize({ debug: true });
5.3 ES 模块与 declare 的结合
typescript
// 声明 ES 模块
declare module 'modern-es-module' {
// 命名导出
export const VERSION: string;
export const DEFAULT_CONFIG: Config;
export interface Config {
apiUrl: string;
retries: number;
cache: boolean;
}
export function createClient(config?: Partial<Config>): Client;
export class Client {
constructor(config: Config);
fetch<T = any>(endpoint: string): Promise<T>;
post<T = any>(endpoint: string, data: any): Promise<T>;
}
// 默认导出
export default createClient;
// 重新导出
export { createClient as createApiClient };
}
// 使用示例
import createClient, { VERSION, DEFAULT_CONFIG } from 'modern-es-module';
console.log(`Using version ${VERSION}`);
const client = createClient({
...DEFAULT_CONFIG,
apiUrl: 'https://api.example.com'
});
client.fetch('/users').then(users => {
console.log(users);
});
第六部分:环境声明与三斜线指令
6.1 环境声明文件 (.d.ts)
typescript
// types/global.d.ts - 全局声明文件
declare global {
// 扩展全局命名空间
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
SENTRY_DSN?: string;
ANALYTICS_ID?: string;
}
}
// 扩展全局接口
interface ImportMeta {
env: {
MODE: string;
BASE_URL: string;
DEV: boolean;
PROD: boolean;
SSR: boolean;
};
}
// 声明全局变量
var __DEVTOOLS__: {
log: (message: string, data?: any) => void;
time: (label: string) => void;
timeEnd: (label: string) => void;
};
}
// 确保这个文件被当作模块
export {};
6.2 三斜线指令详解
typescript
// 三斜线指令的四种类型
// 1. 引用类型声明 /// <reference types="..." />
/// <reference types="node" />
/// <reference types="react" />
/// <reference types="webpack/module" />
// 2. 引用路径 /// <reference path="..." />
/// <reference path="./custom-types.d.ts" />
/// <reference path="../shared/types.d.ts" />
// 3. 引用库 /// <reference lib="..." />
/// <reference lib="es2015" />
/// <reference lib="dom" />
/// <reference lib="esnext.asynciterable" />
// 4. AMD 指令 /// <amd-module name="..." /> 和 /// <amd-dependency path="..." />
/// <amd-module name="MyModule" />
/// <amd-dependency path="css!./styles.css" />
// 实际使用示例
// types/global-setup.d.ts
/// <reference types="node" />
/// <reference types="express" />
/// <reference path="./custom.d.ts" />
/// <reference lib="es2015.promise" />
// 现在可以使用 Node.js、Express 和自定义类型了
declare module 'my-module' {
import { Request } from 'express';
import { EventEmitter } from 'events';
export interface MyRequest extends Request {
user?: {
id: string;
role: string;
};
}
export class MyEmitter extends EventEmitter {
// ...
}
}
第七部分:高级技巧与模式
7.1 类型守卫与 declare 的组合
typescript
// 声明自定义类型守卫
declare function isString(value: unknown): value is string;
declare function isNumber(value: unknown): value is number;
declare function isArray<T>(value: unknown): value is T[];
declare function isPromise<T>(value: unknown): value is Promise<T>;
// 声明品牌类型(Branded Types)
declare type Brand<T, BrandName extends string> = T & {
readonly __brand: BrandName;
};
// 具体品牌类型
declare type Email = Brand<string, 'Email'>;
declare type UUID = Brand<string, 'UUID'>;
declare type PositiveNumber = Brand<number, 'PositiveNumber'>;
// 品牌类型验证函数
declare function validateEmail(email: string): email is Email;
declare function validateUUID(uuid: string): uuid is UUID;
declare function validatePositiveNumber(num: number): num is PositiveNumber;
// 使用示例
function processUser(email: string, id: string) {
if (validateEmail(email) && validateUUID(id)) {
// 在这里,TypeScript 知道 email 是 Email 类型,id 是 UUID 类型
sendWelcomeEmail(email); // 类型安全!
trackUser(id); // 类型安全!
}
}
function sendWelcomeEmail(email: Email) {
// 只能接受有效的 Email 类型
}
function trackUser(id: UUID) {
// 只能接受有效的 UUID 类型
}
7.2 声明高阶类型工具
typescript
// 声明深度只读类型
declare type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
// 声明深度可选类型
declare type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object
? DeepPartial<T[K]>
: T[K];
};
// 声明深度必需类型
declare type DeepRequired<T> = {
[K in keyof T]-?: T[K] extends object
? DeepRequired<T[K]>
: T[K];
};
// 声明键值对展开类型
declare type Flatten<T> = T extends object
? { [K in keyof T]: Flatten<T[K]> }
: T;
// 声明条件类型工具
declare type If<Condition, Then, Else> = Condition extends true
? Then
: Else;
declare type Equals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false;
// 声明函数类型工具
declare type Parameters<T> = T extends (...args: infer P) => any ? P : never;
declare type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
declare type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
declare type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;
// 声明异步函数工具
declare type AsyncReturnType<T> =
T extends (...args: any[]) => Promise<infer R> ? R :
T extends (...args: any[]) => infer R ? R : never;
// 声明迭代器工具
declare type ElementType<T> =
T extends (infer U)[] ? U :
T extends readonly (infer U)[] ? U :
T extends Set<infer U> ? U :
T extends Map<any, infer V> ? V :
T extends Promise<infer U> ? U :
T extends Iterable<infer U> ? U :
T extends AsyncIterable<infer U> ? U :
never;
第八部分:实战案例:构建企业级声明系统
8.1 企业级配置系统声明
typescript
// types/config.d.ts - 企业配置声明
declare namespace EnterpriseConfig {
// 基础配置
export interface BaseConfig {
appName: string;
version: string;
environment: 'development' | 'staging' | 'production';
}
// 功能标志
export interface FeatureFlags {
analytics: boolean;
sentry: boolean;
cdn: boolean;
pwa: boolean;
ssr: boolean;
}
// API 配置
export interface ApiConfig {
baseUrl: string;
timeout: number;
retries: number;
endpoints: {
auth: string;
users: string;
products: string;
orders: string;
};
}
// 第三方服务配置
export interface ThirdPartyConfig {
sentry?: {
dsn: string;
environment: string;
};
googleAnalytics?: {
trackingId: string;
anonymizeIp: boolean;
};
stripe?: {
publishableKey: string;
apiVersion: string;
};
}
// 完整配置
export type FullConfig = BaseConfig & {
features: FeatureFlags;
api: ApiConfig;
thirdParty: ThirdPartyConfig;
};
// 配置加载器
export interface ConfigLoader {
load(): Promise<FullConfig>;
get<K extends keyof FullConfig>(key: K): FullConfig[K];
set<K extends keyof FullConfig>(key: K, value: FullConfig[K]): void;
watch<K extends keyof FullConfig>(
key: K,
callback: (value: FullConfig[K]) => void
): () => void;
}
// 全局配置实例
export const config: ConfigLoader;
// 环境变量类型
export type EnvVars = {
NODE_ENV: string;
API_URL: string;
SENTRY_DSN?: string;
GA_TRACKING_ID?: string;
[key: string]: string | undefined;
};
}
// 使用示例
const apiUrl = EnterpriseConfig.config.get('api').baseUrl;
const features = EnterpriseConfig.config.get('features');
if (features.analytics) {
// 初始化分析
}
8.2 微前端架构中的类型声明
typescript
// types/microfrontend.d.ts - 微前端类型声明
declare namespace MicroFrontend {
// 微应用定义
export interface MicroApp {
name: string;
version: string;
entry: string;
activeRule: string | ((location: Location) => boolean);
props?: Record<string, any>;
container?: string | HTMLElement;
}
// 生命周期
export interface Lifecycle {
bootstrap: () => Promise<void>;
mount: (props: any) => Promise<void>;
unmount: () => Promise<void>;
update?: (props: any) => Promise<void>;
}
// 共享依赖
export interface SharedDependencies {
react: {
version: string;
get: () => Promise<any>;
loaded?: boolean;
};
'react-dom': {
version: string;
get: () => Promise<any>;
loaded?: boolean;
};
vue?: {
version: string;
get: () => Promise<any>;
loaded?: boolean;
};
}
// 通信机制
export interface EventBus {
on: (event: string, handler: Function) => void;
off: (event: string, handler?: Function) => void;
emit: (event: string, ...args: any[]) => void;
once: (event: string, handler: Function) => void;
}
// 状态共享
export interface GlobalState {
get: <T = any>(key: string) => T | undefined;
set: <T = any>(key: string, value: T) => void;
subscribe: (key: string, callback: (value: any) => void) => () => void;
}
// 微前端管理器
export interface MicroFrontendManager {
// 注册微应用
registerApp(app: MicroApp): Promise<void>;
// 启动微前端
start(options?: {
prefetch?: boolean;
sandbox?: boolean;
singular?: boolean;
fetch?: typeof window.fetch;
}): Promise<void>;
// 获取应用状态
getAppStatus(name: string): 'NOT_LOADED' | 'LOADING' | 'NOT_BOOTSTRAPPED' | 'BOOTSTRAPPING' | 'NOT_MOUNTED' | 'MOUNTING' | 'MOUNTED' | 'UNMOUNTING' | 'UPDATING';
// 手动加载应用
loadApp(name: string): Promise<void>;
// 手动挂载应用
mountApp(name: string, props?: any): Promise<void>;
// 手动卸载应用
unmountApp(name: string): Promise<void>;
// 更新应用
updateApp(name: string, props: any): Promise<void>;
}
// 全局实例
export const manager: MicroFrontendManager;
export const eventBus: EventBus;
export const globalState: GlobalState;
export const sharedDeps: SharedDependencies;
}
// 使用示例
MicroFrontend.manager.registerApp({
name: 'dashboard',
version: '1.0.0',
entry: '//localhost:3001/app.js',
activeRule: '/dashboard'
});
MicroFrontend.eventBus.on('user-login', (user) => {
MicroFrontend.globalState.set('currentUser', user);
});
第九部分:调试与测试 declare 声明
9.1 测试类型声明
typescript
// tests/type-declarations.test.ts
import { expectType, expectError, expectAssignable } from 'tsd';
// 测试全局变量
expectType<string>(__VERSION__);
expectError(__VERSION__ = 123); // 应该报错,因为 VERSION 是常量
// 测试全局函数
expectType<void>(trackEvent('page_view', { path: '/' }));
expectError(trackEvent(123)); // 第一个参数应该是字符串
// 测试全局类
const widget = new ExternalWidget(document.createElement('div'));
expectType<void>(widget.render({}));
expectError(widget.render()); // 需要参数
// 测试接口扩展
const div = document.createElement('div');
expectType<HTMLDivElement>(div);
expectAssignable<HTMLElement>(div); // HTMLDivElement 应该能赋值给 HTMLElement
// 测试条件类型
type Test1 = If<true, string, number>;
expectType<string>({} as Test1);
type Test2 = If<false, string, number>;
expectType<number>({} as Test2);
// 测试品牌类型
declare const email: Email;
expectType<string>(email); // Email 可以赋值给 string
expectError<string>({} as Email); // 但不是所有 string 都是 Email
9.2 使用 TypeScript 编译器 API 验证声明
typescript
// scripts/validate-declarations.ts
import * as ts from 'typescript';
import * as fs from 'fs';
import * as path from 'path';
interface ValidationResult {
declarations: string[];
errors: string[];
warnings: string[];
isValid: boolean;
}
class DeclarationValidator {
constructor(private declarationDir: string) {}
validate(): ValidationResult {
const declarations = this.collectDeclarations();
const result: ValidationResult = {
declarations: [],
errors: [],
warnings: [],
isValid: true
};
for (const declaration of declarations) {
const validation = this.validateDeclaration(declaration);
result.declarations.push(declaration);
if (validation.errors.length > 0) {
result.errors.push(...validation.errors);
result.isValid = false;
}
if (validation.warnings.length > 0) {
result.warnings.push(...validation.warnings);
}
}
return result;
}
private collectDeclarations(): string[] {
const files: string[] = [];
const walk = (dir: string) => {
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
walk(fullPath);
} else if (item.endsWith('.d.ts')) {
files.push(fullPath);
}
}
};
walk(this.declarationDir);
return files;
}
private validateDeclaration(filePath: string): { errors: string[]; warnings: string[] } {
const program = ts.createProgram([filePath], {
strict: true,
noImplicitAny: true,
skipLibCheck: false
});
const sourceFile = program.getSourceFile(filePath);
const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
const errors: string[] = [];
const warnings: string[] = [];
for (const diagnostic of diagnostics) {
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
if (diagnostic.category === ts.DiagnosticCategory.Error) {
errors.push(`Error in ${filePath}: ${message}`);
} else if (diagnostic.category === ts.DiagnosticCategory.Warning) {
warnings.push(`Warning in ${filePath}: ${message}`);
}
}
return { errors, warnings };
}
generateReport(result: ValidationResult): string {
let report = `# TypeScript 声明验证报告\n\n`;
report += `生成时间: ${new Date().toISOString()}\n`;
report += `验证文件数: ${result.declarations.length}\n`;
report += `状态: ${result.isValid ? '✅ 通过' : '❌ 失败'}\n\n`;
if (result.errors.length > 0) {
report += `## 错误列表\n\n`;
result.errors.forEach(error => {
report += `- ${error}\n`;
});
report += '\n';
}
if (result.warnings.length > 0) {
report += `## 警告列表\n\n`;
result.warnings.forEach(warning => {
report += `- ${warning}\n`;
});
report += '\n';
}
report += `## 验证的文件\n\n`;
result.declarations.forEach(file => {
report += `- ${file}\n`;
});
return report;
}
}
第十部分:最佳实践与性能优化
10.1 declare 的性能影响
typescript
// 性能优化技巧
// 1. 避免过度使用全局声明
// 不好的例子:太多全局变量
declare const A: string;
declare const B: string;
declare const C: string;
declare const D: string;
// ... 数十个全局声明
// 好的例子:使用命名空间
declare namespace Constants {
export const A: string;
export const B: string;
export const C: string;
export const D: string;
}
// 2. 避免深度嵌套的条件类型
// 不好的例子:深度递归
declare type DeepTransform<T> =
T extends Array<infer U>
? Array<DeepTransform<U>>
: T extends object
? { [K in keyof T]: DeepTransform<T[K]> }
: T;
// 好的例子:限制深度或使用简单类型
declare type ShallowTransform<T> = {
[K in keyof T]: T[K];
};
// 3. 使用接口继承而不是交叉类型
// 不好的例子:大量交叉类型
declare type Combined = A & B & C & D & E;
// 好的例子:接口继承
declare interface Combined extends A, B, C, D, E {}
// 4. 懒加载类型声明
// 使用条件导出,只在需要时加载
declare module 'large-library' {
export type HeavyType = /* 复杂的类型定义 */;
// 提供轻量级的接口
export interface LightInterface {
simpleMethod(): void;
}
// 需要时再导入完整类型
export function getHeavyType(): Promise<HeavyType>;
}
10.2 声明文件组织策略
typescript
// 推荐的目录结构
types/
├── global.d.ts # 全局声明
├── environment.d.ts # 环境相关声明
├── third-party/ # 第三方库声明
│ ├── legacy-lib.d.ts
│ ├── jquery-plugins.d.ts
│ └── custom-charts.d.ts
├── extensions/ # 扩展声明
│ ├── window.d.ts
│ ├── document.d.ts
│ └── array.d.ts
├── utils/ # 工具类型
│ ├── types.d.ts
│ ├── guards.d.ts
│ └── predicates.d.ts
├── config/ # 配置类型
│ └── app-config.d.ts
└── modules/ # 模块声明
├── api-client.d.ts
└── auth.d.ts
// tsconfig.json 配置
{
"compilerOptions": {
"typeRoots": ["./types", "./node_modules/@types"],
"types": ["node", "express", "react"], // 显式包含的类型
"skipLibCheck": false // 推荐关闭以检查声明文件
},
"include": [
"src/**/*",
"types/**/*" // 包含自定义声明
]
}
结语:declare 的艺术与科学
掌握 declare 关键字是 TypeScript 高级开发者的标志性技能。它不仅仅是技术细节,更是一种类型系统设计哲学的体现。
核心要点总结:
- 信任边界管理:declare 定义了 TypeScript 信任外部世界的边界
- 类型系统扩展:通过 declare 可以无缝集成 JavaScript 生态
- 架构设计工具:良好的全局类型声明是系统架构的重要组成部分
- 开发体验优化:为团队提供一致的类型安全和智能提示
成为 declare 大师的三个层次:
层次一:使用者
- 理解 declare 的基本语法
- 能使用现有的类型声明
- 知道如何查找和安装 @types 包
层次二:设计者
- 能为第三方库创建准确的类型声明
- 理解声明合并和模块扩展
- 能设计项目级的类型声明架构
层次三:架构师
- 理解 declare 的性能影响并优化
- 设计跨项目的类型共享方案
- 建立类型声明的规范和流程
- 推动类型系统的演进和优化
未来展望:
- 类型推导自动化:AI 辅助生成和验证声明文件
- 运行时类型集成:声明文件与运行时验证的深度结合
- 跨语言类型同步:不同编程语言间的类型声明共享
- 类型驱动开发:从类型声明自动生成文档、测试和实现
记住:好的类型声明不是终点,而是高质量代码的起点。通过精心设计的 declare 声明,你不仅能让 TypeScript 更好地理解你的代码,还能为整个团队建立清晰的契约和规范。