====== 第十一章 类型声明文件 ======
类型声明文件(.d.ts)是TypeScript项目的重要组成部分,它描述了JavaScript库的类型信息,使TypeScript能够在使用JavaScript库时提供类型检查和智能提示。
===== 11.1 声明文件基础 =====
==== 11.1.1 声明文件的作用 ====
类型声明文件只包含类型信息,不包含实现:
- 描述现有JavaScript代码的类型
- 为第三方库提供类型定义
- 扩展全局对象的类型
// mylib.d.ts - 声明文件示例
declare module "mylib" {
export interface User {
id: number;
name: string;
}
export function createUser(name: string): User;
export const version: string;
}
==== 11.1.2 声明文件位置 ====
TypeScript按以下顺序查找声明文件:
1. **内置类型**:lib.d.ts等
2. **package.json中的types字段**:指定入口声明文件
3. **@types包**:DefinitelyTyped社区维护的类型
4. **项目中声明文件**:
- tsconfig.json中files/include指定的文件
- 与.js文件同名的.d.ts文件
- 项目根目录的.d.ts文件
===== 11.2 创建声明文件 =====
==== 11.2.1 声明语句类型 ====
// types.d.ts
// 声明变量
declare const VERSION: string;
declare let config: { apiUrl: string };
// 声明函数
declare function greet(name: string): string;
declare function process(callback: (result: string) => void): void;
// 声明类
declare class Animal {
constructor(name: string);
name: string;
move(distance: number): void;
}
// 声明枚举
declare enum Direction {
Up,
Down,
Left,
Right
}
// 声明模块
export interface Config {
debug: boolean;
}
// 声明命名空间
declare namespace MyLib {
function doSomething(): void;
const version: string;
}
==== 11.2.2 为JavaScript库编写声明 ====
假设有一个JavaScript库my-utils.js:
// my-utils.js
function add(a, b) {
return a + b;
}
class Calculator {
constructor() {
this.result = 0;
}
multiply(x) {
return this.result *= x;
}
}
module.exports = { add, Calculator };
对应的声明文件:
// my-utils.d.ts
export function add(a: number, b: number): number;
export class Calculator {
result: number;
constructor();
multiply(x: number): number;
}
==== 11.2.3 UMD模块声明 ====
// 支持CommonJS/AMD/全局变量
declare namespace MyLibrary {
function doSomething(): void;
const version: string;
}
export = MyLibrary;
export as namespace MyLibrary;
===== 11.3 模块声明模式 =====
==== 11.3.1 全局库声明 ====
用于直接通过script标签引入的库:
// jquery.d.ts
declare const $: JQueryStatic;
declare const jQuery: JQueryStatic;
interface JQueryStatic {
(selector: string): JQuery;
ajax(url: string, settings?: JQuery.AjaxSettings): JQuery.jqXHR;
fn: any;
extend(deep: boolean, target: any, ...objects: any[]): any;
}
interface JQuery {
addClass(className: string): this;
attr(attributeName: string): string | undefined;
attr(attributeName: string, value: string | number): this;
on(events: string, handler: (event: JQuery.Event) => void): this;
off(events: string, handler?: (event: JQuery.Event) => void): this;
}
declare namespace JQuery {
interface AjaxSettings {
url?: string;
method?: string;
data?: any;
success?: (data: any, textStatus: string, jqXHR: jqXHR) => void;
}
interface Event {
type: string;
target: Element;
preventDefault(): void;
stopPropagation(): void;
}
interface jqXHR {
done(callback: (data: any, textStatus: string, jqXHR: jqXHR) => void): this;
fail(callback: (jqXHR: jqXHR, textStatus: string, errorThrown: string) => void): this;
}
}
==== 11.3.2 模块库声明 ====
// lodash.d.ts
declare module "lodash" {
export function chunk(array: T[], size: number): T[][];
export function debounce any>(
func: T,
wait?: number,
options?: DebounceSettings
): T & { cancel(): void; flush(): ReturnType };
export function throttle any>(
func: T,
wait?: number,
options?: ThrottleSettings
): T & { cancel(): void; flush(): ReturnType };
export function cloneDeep(value: T): T;
export function merge(
object: TObject,
source: TSource
): TObject & TSource;
export function groupBy(
collection: T[],
iteratee: ((value: T) => string) | string
): { [key: string]: T[] };
interface DebounceSettings {
leading?: boolean;
trailing?: boolean;
maxWait?: number;
}
interface ThrottleSettings {
leading?: boolean;
trailing?: boolean;
}
}
==== 11.3.3 插件扩展声明 ====
为现有库添加方法:
// jquery-myplugin.d.ts
///
declare global {
interface JQuery {
myPlugin(options?: MyPluginOptions): this;
greenify(): this;
}
interface JQueryStatic {
myPlugin: {
defaults: MyPluginOptions;
setDefaults(options: MyPluginOptions): void;
};
}
}
interface MyPluginOptions {
color?: string;
backgroundColor?: string;
}
export {};
===== 11.4 高级声明技术 =====
==== 11.4.1 条件类型与推断 ====
// 声明条件类型
type ElementType = T extends (infer E)[] ? E : T;
// 声明映射类型
type Readonly = {
readonly [P in keyof T]: T[P];
};
type Partial = {
[P in keyof T]?: T[P];
};
type Required = {
[P in keyof T]-?: T[P];
};
type Pick = {
[P in K]: T[P];
};
type Record = {
[P in K]: T;
};
==== 11.4.2 函数重载声明 ====
// 复杂函数重载
declare function fetch(url: string): Promise;
declare function fetch(
url: string,
options: RequestInit
): Promise;
declare function fetch(
input: RequestInfo,
init?: RequestInit
): Promise;
// 基于返回类型的重载
declare function createElement(tagName: "a"): HTMLAnchorElement;
declare function createElement(tagName: "canvas"): HTMLCanvasElement;
declare function createElement(tagName: "div"): HTMLDivElement;
declare function createElement(tagName: "input"): HTMLInputElement;
declare function createElement(tagName: "span"): HTMLSpanElement;
declare function createElement(tagName: string): HTMLElement;
==== 11.4.3 泛型约束声明 ====
// 泛型接口
declare interface ApiResponse {
data: T;
status: number;
message: string;
}
declare interface PaginatedResponse {
items: T[];
total: number;
page: number;
pageSize: number;
}
// 泛型函数约束
declare function find(
array: T[],
key: K,
value: T[K]
): T | undefined;
declare function sortBy(
array: T[],
key: K
): T[];
declare function groupBy(
array: T[],
key: K
): Map;
===== 11.5 发布类型定义 =====
==== 11.5.1 与npm包一起发布 ====
在package.json中指定types字段:
{
"name": "my-library",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./package.json": "./package.json"
},
"files": [
"dist",
"src"
]
}
==== 11.5.2 提交到DefinitelyTyped ====
对于没有内置类型的流行库,可以提交到@types组织:
1. Fork DefinitelyTyped/DefinitelyTyped
2. 在types目录创建包文件夹
3. 编写index.d.ts和测试文件
4. 提交Pull Request
结构:
types/my-library/
├── index.d.ts # 主声明文件
├── my-library-tests.ts # 测试文件
├── tsconfig.json # 配置
└── package.json # {"private": true}
===== 11.6 声明文件最佳实践 =====
==== 11.6.1 避免常见错误 ====
// ❌ 错误:在声明文件中使用实现
export function add(a: number, b: number) {
return a + b;
}
// ✅ 正确:只声明类型
export function add(a: number, b: number): number;
// ❌ 错误:导入使用require
import fs = require("fs");
// ✅ 正确:使用ES模块语法
import * as fs from "fs";
import type { Readable } from "stream";
// ❌ 错误:使用any
export function process(data: any): any;
// ✅ 正确:使用unknown或具体类型
export function process(data: T): Processed;
==== 11.6.2 文档与注释 ====
/**
* 用户管理类
* @example
* ```ts
* const userManager = new UserManager();
* const user = await userManager.create({ name: "John" });
* ```
*/
export class UserManager {
/**
* 创建新用户
* @param data - 用户数据
* @returns 创建的用户对象
* @throws {ValidationError} 数据验证失败时抛出
*/
create(data: CreateUserData): Promise;
/**
* 根据ID查找用户
* @param id - 用户ID
* @returns 用户对象,不存在时返回null
*/
findById(id: string): Promise;
}
/** 创建用户的数据 */
export interface CreateUserData {
/** 用户名,3-20个字符 */
name: string;
/** 邮箱地址 */
email: string;
/** 可选的个人简介 */
bio?: string;
}
==== 11.6.3 版本兼容性 ====
// 使用ts版本标记
// TypeScript Version: 4.0
// 条件类型特性
// TypeScript Version: 2.8
// 模板字面量类型
// TypeScript Version: 4.1
// 在package.json中指定
{
"name": "@types/mylib",
"version": "1.0.0",
"typescript": {
"version": ">=4.0.0"
}
}
===== 11.7 实际案例 =====
==== 11.7.1 Express类型定义 ====
// express 类型定义简化版
declare namespace Express {
interface Request {
body: any;
params: { [key: string]: string };
query: { [key: string]: string | string[] };
headers: IncomingHttpHeaders;
ip?: string;
path: string;
protocol: string;
secure: boolean;
xhr: boolean;
}
interface Response {
status(code: number): this;
json(body?: any): this;
send(body?: any): this;
redirect(url: string): this;
render(view: string, options?: object): void;
}
interface NextFunction {
(err?: any): void;
}
interface Application {
use(...handlers: RequestHandler[]): this;
get(path: string, ...handlers: RequestHandler[]): this;
post(path: string, ...handlers: RequestHandler[]): this;
listen(port: number, callback?: () => void): Server;
}
}
type RequestHandler = (
req: Express.Request,
res: Express.Response,
next: Express.NextFunction
) => void | Promise;
declare function express(): Express.Application;
declare namespace express {
export function json(): RequestHandler;
export function urlencoded(options?: { extended?: boolean }): RequestHandler;
export function static(root: string, options?: any): RequestHandler;
}
export = express;
==== 11.7.2 数据库驱动类型 ====
// 通用数据库驱动类型
export interface DatabaseConnection {
query(sql: string, params?: any[]): Promise;
execute(sql: string, params?: any[]): Promise<{ affectedRows: number }>;
transaction(callback: (trx: Transaction) => Promise): Promise;
close(): Promise;
}
export interface Transaction extends DatabaseConnection {
rollback(): Promise;
commit(): Promise;
}
export interface QueryBuilder {
where(column: K, value: T[K]): this;
where(column: K, operator: string, value: any): this;
where(condition: Partial): this;
select(...columns: K[]): QueryBuilder>;
orderBy(column: keyof T, direction?: "asc" | "desc"): this;
limit(count: number): this;
offset(count: number): this;
first(): Promise;
get(): Promise;
count(): Promise;
insert(data: Omit): Promise;
update(data: Partial): Promise;
delete(): Promise;
}
===== 11.8 工具与资源 =====
==== 11.8.1 自动生成声明 ====
# 从TypeScript源码生成声明
tsc --declaration --emitDeclarationOnly
# 使用dts-bundle打包声明
npx dts-bundle --name mylib --main dist/index.d.ts
# 从JSDoc生成
tsc --allowJs --declaration --emitDeclarationOnly
==== 11.8.2 测试声明文件 ====
// mylib-tests.ts
import { add, multiply, Calculator } from "mylib";
// $ExpectType number
const sum = add(1, 2);
// $ExpectError
add("1", "2");
// 测试类
const calc = new Calculator();
// $ExpectType number
calc.multiply(5);
===== 11.9 小结 =====
类型声明文件是TypeScript生态的关键组成部分:
1. **声明语法**:declare关键字用于声明变量、函数、类、枚举等
2. **模块模式**:支持全局库、模块库和UMD模式的声明
3. **高级类型**:条件类型、映射类型、泛型约束等高级特性
4. **发布策略**:与npm包一起发布或提交到DefinitelyTyped
5. **最佳实践**:避免any、添加文档注释、保持版本兼容
掌握声明文件编写对于TypeScript开发者来说是一项重要技能,特别是在使用JavaScript库或为库添加TypeScript支持时。