第7章:声明文件(Declaration Files)
一、导读
1.1 本章学习目标
第7章讲解 TypeScript 的声明文件(.d.ts)——如何为 JavaScript 库编写类型定义,以及如何发布和管理类型声明。
本章的核心学习目标包括:
- 理解声明文件的作用和格式
- 掌握 `declare` 关键字的各种用法
- 学会为 JavaScript 库编写类型定义
- 理解模块声明和全局类型声明
- 掌握 DefinitelyTyped 和 `@types` 包的使用
1.2 为什么本章如此重要
不是所有的 JavaScript 库都自带 TypeScript 类型定义。当你使用一个纯 JavaScript 库时,TypeScript 会报类型错误。声明文件让你能够为这些库提供类型信息,享受完整的类型检查和代码补全。
即使你不为第三方库编写类型定义,理解声明文件的原理也能帮助你更好地使用 TypeScript——理解类型是如何工作的,理解为什么某些代码能编译而某些不能。
1.3 核心问题引导
.d.ts 文件和普通 .ts 文件有什么区别?
declare 关键字的作用是什么?它能声明哪些东西?
如何为一个没有类型定义的 JavaScript 库编写类型声明?
@types 包是什么?DefinitelyTyped 是如何工作的?
模块声明和全局声明有什么区别?
二、核心概念详解
2.1 声明文件基础
// math.d.ts
declare function add(a: number, b: number): number;
declare function multiply(a: number, b: number): number;
declare const PI: number;
declare class Calculator {
constructor(initialValue?: number);
add(n: number): Calculator;
subtract(n: number): Calculator;
result(): number;
}2.2 模块声明
// lodash.d.ts
declare module "lodash" {
export function chunk<T>(array: T[], size?: number): T[][];
export function compact<T>(array: (T | null | undefined | false)[]): T[];
export function flatten<T>(array: (T | T[])[]): T[];
export function map<T, R>(
collection: T[],
iteratee: (value: T, index: number) => R
): R[];
export function filter<T>(
collection: T[],
predicate: (value: T) => boolean
): T[];
}2.3 全局类型声明
// global.d.ts
declare global {
interface Window {
myApp: {
version: string;
config: AppConfig;
};
}
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: "development" | "production" | "test";
API_URL: string;
DATABASE_URL: string;
}
}
}
export {};2.4 为 JavaScript 库编写类型
// types/event-emitter.d.ts
declare module "tiny-event-emitter" {
type EventHandler = (...args: any[]) => void;
export class EventEmitter {
on(event: string, handler: EventHandler): this;
off(event: string, handler: EventHandler): this;
emit(event: string, ...args: any[]): boolean;
once(event: string, handler: EventHandler): this;
removeAllListeners(event?: string): this;
listenerCount(event: string): number;
}
export default EventEmitter;
}2.5 泛型声明
declare module "my-store" {
export interface Store<T> {
getState(): T;
setState(partial: Partial<T>): void;
subscribe(listener: (state: T) => void): () => void;
}
export function createStore<T>(initialState: T): Store<T>;
export function useStore<T, R>(
store: Store<T>,
selector: (state: T) => R
): R;
}三、实战案例
3.1 案例一:为 Express 中间件编写类型
declare module "express-rate-limiter" {
import { Request, Response, NextFunction } from "express";
interface RateLimitOptions {
windowMs: number;
max: number;
message?: string | object;
statusCode?: number;
keyGenerator?: (req: Request) => string;
skip?: (req: Request) => boolean;
handler?: (req: Request, res: Response, next: NextFunction) => void;
}
export function rateLimit(options: RateLimitOptions): (
req: Request,
res: Response,
next: NextFunction
) => void;
export default rateLimit;
}3.2 案例二:为 Web Worker 编写类型
// worker.d.ts
declare module "worker-loader!*" {
class WebpackWorker extends Worker {
constructor();
}
export default WebpackWorker;
}
declare class ImageProcessor {
constructor();
process(imageData: ImageData): Promise<ImageData>;
terminate(): void;
}
declare namespace ImageProcessor {
interface Options {
width: number;
height: number;
filters: FilterType[];
}
type FilterType = "blur" | "sharpen" | "grayscale" | "sepia";
}3.3 案例三:完整的库类型定义
declare module "simple-orm" {
export interface ColumnOptions {
primary?: boolean;
autoIncrement?: boolean;
nullable?: boolean;
unique?: boolean;
default?: any;
}
export interface TableOptions {
tableName?: string;
timestamps?: boolean;
}
export function Table(options?: TableOptions): ClassDecorator;
export function Column(options?: ColumnOptions): PropertyDecorator;
export class Repository<T> {
findById(id: number | string): Promise<T | null>;
findAll(): Promise<T[]>;
find(where: Partial<T>): Promise<T[]>;
create(data: Omit<T, "id">): Promise<T>;
update(id: number | string, data: Partial<T>): Promise<T>;
delete(id: number | string): Promise<boolean>;
count(where?: Partial<T>): Promise<number>;
}
export function getRepository<T>(entity: new () => T): Repository<T>;
}四、本章小结
声明文件:.d.ts 文件为 JavaScript 代码提供类型信息。
declare 关键字:声明变量、函数、类、模块等,不生成运行时代码。
模块声明:declare module "xxx" 为第三方模块提供类型。
全局声明:declare global 扩展全局类型。
DefinitelyTyped:社区维护的 @types 包仓库。
五、思考与练习
.d.ts 文件中的 declare 和普通 .ts 文件中的声明有什么区别?
如何为一个使用 CommonJS 的 JavaScript 库编写类型声明?
编写一个完整的 .d.ts 文件,为一个简单的工具库提供类型定义。