类型声明文件(.d.ts)是TypeScript项目的重要组成部分,它描述了JavaScript库的类型信息,使TypeScript能够在使用JavaScript库时提供类型检查和智能提示。
类型声明文件只包含类型信息,不包含实现: - 描述现有JavaScript代码的类型 - 为第三方库提供类型定义 - 扩展全局对象的类型
// mylib.d.ts - 声明文件示例
declare module "mylib" {
export interface User {
id: number;
name: string;
}
export function createUser(name: string): User;
export const version: string;
}
TypeScript按以下顺序查找声明文件:
1. 内置类型:lib.d.ts等 2. package.json中的types字段:指定入口声明文件 3. @types包:DefinitelyTyped社区维护的类型 4. 项目中声明文件:
// 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;
}
假设有一个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;
}
// 支持CommonJS/AMD/全局变量
declare namespace MyLibrary {
function doSomething(): void;
const version: string;
}
export = MyLibrary;
export as namespace MyLibrary;
用于直接通过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;
}
}
// lodash.d.ts
declare module "lodash" {
export function chunk<T>(array: T[], size: number): T[][];
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait?: number,
options?: DebounceSettings
): T & { cancel(): void; flush(): ReturnType<T> };
export function throttle<T extends (...args: any[]) => any>(
func: T,
wait?: number,
options?: ThrottleSettings
): T & { cancel(): void; flush(): ReturnType<T> };
export function cloneDeep<T>(value: T): T;
export function merge<TObject, TSource>(
object: TObject,
source: TSource
): TObject & TSource;
export function groupBy<T>(
collection: T[],
iteratee: ((value: T) => string) | string
): { [key: string]: T[] };
interface DebounceSettings {
leading?: boolean;
trailing?: boolean;
maxWait?: number;
}
interface ThrottleSettings {
leading?: boolean;
trailing?: boolean;
}
}
为现有库添加方法:
// jquery-myplugin.d.ts
/// <reference types="jquery" />
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 {};
// 声明条件类型
type ElementType<T> = T extends (infer E)[] ? E : T;
// 声明映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Record<K extends keyof any, T> = {
[P in K]: T;
};
// 复杂函数重载 declare function fetch(url: string): Promise<Response>; declare function fetch( url: string, options: RequestInit ): Promise<Response>; declare function fetch( input: RequestInfo, init?: RequestInit ): Promise<Response>; // 基于返回类型的重载 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;
// 泛型接口
declare interface ApiResponse<T = any> {
data: T;
status: number;
message: string;
}
declare interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
pageSize: number;
}
// 泛型函数约束
declare function find<T, K extends keyof T>(
array: T[],
key: K,
value: T[K]
): T | undefined;
declare function sortBy<T, K extends keyof T>(
array: T[],
key: K
): T[];
declare function groupBy<T, K extends keyof T>(
array: T[],
key: K
): Map<T[K], T[]>;
在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"
]
}
对于没有内置类型的流行库,可以提交到@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}
// ❌ 错误:在声明文件中使用实现
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<T>(data: T): Processed<T>;
/**
* 用户管理类
* @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<User>;
/**
* 根据ID查找用户
* @param id - 用户ID
* @returns 用户对象,不存在时返回null
*/
findById(id: string): Promise<User | null>;
}
/** 创建用户的数据 */
export interface CreateUserData {
/** 用户名,3-20个字符 */
name: string;
/** 邮箱地址 */
email: string;
/** 可选的个人简介 */
bio?: string;
}
// 使用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"
}
}
// 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<void>;
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;
// 通用数据库驱动类型
export interface DatabaseConnection {
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
execute(sql: string, params?: any[]): Promise<{ affectedRows: number }>;
transaction<T>(callback: (trx: Transaction) => Promise<T>): Promise<T>;
close(): Promise<void>;
}
export interface Transaction extends DatabaseConnection {
rollback(): Promise<void>;
commit(): Promise<void>;
}
export interface QueryBuilder<T> {
where<K extends keyof T>(column: K, value: T[K]): this;
where<K extends keyof T>(column: K, operator: string, value: any): this;
where(condition: Partial<T>): this;
select<K extends keyof T>(...columns: K[]): QueryBuilder<Pick<T, K>>;
orderBy(column: keyof T, direction?: "asc" | "desc"): this;
limit(count: number): this;
offset(count: number): this;
first(): Promise<T | null>;
get(): Promise<T[]>;
count(): Promise<number>;
insert(data: Omit<T, "id">): Promise<T>;
update(data: Partial<T>): Promise<number>;
delete(): Promise<number>;
}
# 从TypeScript源码生成声明 tsc --declaration --emitDeclarationOnly # 使用dts-bundle打包声明 npx dts-bundle --name mylib --main dist/index.d.ts # 从JSDoc生成 tsc --allowJs --declaration --emitDeclarationOnly
// 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);
类型声明文件是TypeScript生态的关键组成部分:
1. 声明语法:declare关键字用于声明变量、函数、类、枚举等 2. 模块模式:支持全局库、模块库和UMD模式的声明 3. 高级类型:条件类型、映射类型、泛型约束等高级特性 4. 发布策略:与npm包一起发布或提交到DefinitelyTyped 5. 最佳实践:避免any、添加文档注释、保持版本兼容
掌握声明文件编写对于TypeScript开发者来说是一项重要技能,特别是在使用JavaScript库或为库添加TypeScript支持时。