14. 声明文件(Declaration Files)
1. 概述
声明文件(.d.ts文件)用于描述 JavaScript 库的类型信息,让 TypeScript 能够理解和使用纯 JavaScript 编写的代码。声明文件只包含类型定义,不包含实现代码。
┌─────────────────────────────────────────────────────────────┐ │ 声明文件系统 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 声明文件的作用 │ │ ├── 为 JavaScript 库提供类型信息 │ │ ├── 让 TypeScript 理解全局变量 │ │ ├── 扩展现有模块的类型 │ │ └── 为项目提供统一的类型定义 │ │ │ │ 声明文件来源 │ │ ├── 内置类型:TypeScript 自带(lib.d.ts) │ │ ├── DefinitelyTyped:`@types/*`包 │ │ ├── 库自带:库内置类型定义 │ │ └── 自定义:项目内手写声明 │ │ │ │ 声明语法 │ │ ├──declarevar:声明全局变量 │ │ ├──declarefunction:声明函数 │ │ ├──declareclass:声明类 │ │ ├──declaremodule:声明模块 │ │ ├──declarenamespace:声明命名空间 │ │ └──declareglobal:扩展全局类型 │ │ │ └─────────────────────────────────────────────────────────────┘2. 为什么需要声明文件
2.1 没有声明文件的问题
// utils.js(纯 JavaScript)exportfunctionformatPrice(price,currency='¥'){return`${currency}${price.toFixed(2)}`;}exportconstAPI_URL='https://api.example.com';exportclassUser{constructor(name){this.name=name;}greet(){return`Hello,${this.name}`;}}// app.ts(TypeScript)import{formatPrice,API_URL,User}from'./utils.js';// TypeScript 无法推断类型,所有导入都是 any 类型constprice=formatPrice(99.9);// anyconsturl=API_URL;// anyconstuser=newUser('Alice');// any2.2 添加声明文件
// utils.d.tsdeclaremodule'./utils.js'{exportfunctionformatPrice(price:number,currency?:string):string;exportconstAPI_URL:string;exportclassUser{constructor(name:string);name:string;greet():string;}}3. 声明语法
3.1 declare var / let / const
// global.d.ts// 声明全局变量declarevar__VERSION__:string;declarelet__DEBUG__:boolean;declareconst__API_URL__:string;// 声明全局函数declarefunctionlog(message:string):void;// 声明全局类declareclassGlobalService{staticinstance:GlobalService;getName():string;}3.2 declare function
// 声明函数重载declarefunctioncreateElement(tag:string):HTMLElement;declarefunctioncreateElement(tag:string,props:object):HTMLElement;declarefunctioncreateElement(tag:string,props:object,...children:any[]):HTMLElement;// 声明函数属性declarefunctionfetch(url:string):Promise<Response>;declarenamespacefetch{functionpolyfill():void;constisSupported:boolean;}3.3 declare class
// 声明类declareclassAnimal{constructor(name:string);name:string;speak():void;staticcreate(name:string):Animal;}// 声明抽象类declareabstractclassShape{abstractgetArea():number;getPerimeter():number;}3.4 declare module
// 声明外部模块declaremodule'lodash'{exportfunctionchunk<T>(array:T[],size:number):T[][];exportfunctiondebounce<Textends(...args:any[])=>any>(func:T,wait:number):T;}// 声明模块通配符declaremodule'*.css'{constcontent:{[className:string]:string};exportdefaultcontent;}declaremodule'*.vue'{importtype{DefineComponent}from'vue';constcomponent:DefineComponent<{},{},any>;exportdefaultcomponent;}declaremodule'*.png'{constsrc:string;exportdefaultsrc;}3.5 declare namespace
// 声明命名空间declarenamespaceMyLib{functiondoSomething():void;namespaceUtils{functionhelper():string;}interfaceOptions{debug:boolean;timeout:number;}}// 使用MyLib.doSomething();MyLib.Utils.helper();3.6 declare global
// 扩展全局类型declareglobal{interfaceWindow{myCustomProperty:string;myAPI:{getData():Promise<any>;};}interfaceArray<T>{last():T|undefined;first():T|undefined;}}// 使用window.myCustomProperty='value';constlast=[1,2,3].last();// 34. DefinitelyTyped
4.1 安装类型定义
# 安装 Node.js 类型npminstall--save-dev @types/node# 安装 React 类型npminstall--save-dev @types/react# 安装 Lodash 类型npminstall--save-dev @types/lodash# 安装 Express 类型npminstall--save-dev @types/express4.2 使用示例
// 安装后自动可用,无需额外配置import*asexpressfrom'express';import*as_from'lodash';constapp=express();// 类型正确constchunked=_.chunk([1,2,3,4],2);// number[][]5. 编写声明文件
5.1 为 JavaScript 库编写声明
// my-library.d.ts// 假设有一个 JavaScript 库 my-library// 模块声明declaremodule'my-library'{// 配置接口exportinterfaceConfig{debug?:boolean;timeout?:number;retries?:number;}// 主要函数exportfunctioninit(config:Config):void;exportfunctiongetData<T>(url:string):Promise<T>;exportfunctionpostData<T>(url:string,data:any):Promise<T>;// 类exportclassClient{constructor(config:Config);request<T>(url:string):Promise<T>;close():void;}// 常量exportconstversion:string;exportconstAPI_BASE:string;}5.2 为全局库编写声明
// global-lib.d.ts// 假设有一个全局 JavaScript 库interfaceGlobalLibConfig{mode:'development'|'production';apiUrl:string;}interfaceGlobalLibAPI{start():void;stop():void;getConfig():GlobalLibConfig;}// 声明全局变量declarevarGlobalLib:GlobalLibAPI;declarefunctioninitGlobalLib(config:GlobalLibConfig):void;5.3 扩展现有模块
// express-extensions.d.tsimport'express';declaremodule'express'{interfaceRequest{user?:{id:number;name:string;role:'admin'|'user';};startTime:number;}interfaceResponse{success<T>(data:T):this;error(message:string,code?:number):this;}}6. 声明文件的类型导出
6.1 导出类型
// types/index.d.tsexportinterfaceUser{id:number;name:string;email:string;}exporttypeUserRole='admin'|'user'|'guest';exportconstdefaultUser:User;exportfunctioncreateUser(name:string,email:string):User;6.2 默认导出
// logger.d.tsdeclareclassLogger{constructor(name:string);info(message:string):void;error(message:string):void;warn(message:string):void;}exportdefaultLogger;6.3 混合导出
// utils.d.tsexportconstversion:string;exportfunctionformatDate(date:Date):string;exportfunctionformatNumber(num:number):string;exportdefault{version,formatDate,formatNumber};7. 完整示例:为 API 客户端编写声明
// api-client.js(假设是已有的 JavaScript 代码)// ============ 1. 声明文件 ============// api-client.d.tsdeclaremodule'api-client'{// 请求配置exportinterfaceRequestConfig{method?:'GET'|'POST'|'PUT'|'DELETE';headers?:Record<string,string>;timeout?:number;retries?:number;}// 响应类型exportinterfaceApiResponse<T=any>{success:boolean;data:T;message:string;code:number;timestamp:number;}// 错误类型exportinterfaceApiError{code:number;message:string;details?:any;}// 拦截器exportinterfaceInterceptor{request?:(config:RequestConfig)=>RequestConfig;response?:<T>(response:ApiResponse<T>)=>ApiResponse<T>;error?:(error:ApiError)=>void;}// 客户端类exportclassApiClient{constructor(baseURL:string,config?:RequestConfig);get<T>(url:string,config?:RequestConfig):Promise<ApiResponse<T>>;post<T>(url:string,data?:any,config?:RequestConfig):Promise<ApiResponse<T>>;put<T>(url:string,data?:any,config?:RequestConfig):Promise<ApiResponse<T>>;delete<T>(url:string,config?:RequestConfig):Promise<ApiResponse<T>>;addInterceptor(interceptor:Interceptor):void;removeInterceptor(interceptor:Interceptor):void;setToken(token:string):void;clearToken():void;}// 工厂函数exportfunctioncreateClient(baseURL:string,config?:RequestConfig):ApiClient;// 默认导出exportdefaultApiClient;}// ============ 2. 使用示例 ============// app.tsimportApiClient,{createClient,ApiResponse}from'api-client';interfaceUser{id:number;name:string;email:string;}// 方式1:使用类constclient=newApiClient('https://api.example.com',{timeout:5000,retries:3});client.setToken('your-token');constresponse=awaitclient.get<User[]>('/users');if(response.success){console.log(response.data);// User[]}// 方式2:使用工厂函数constclient2=createClient('https://api.example.com');// 添加拦截器client2.addInterceptor({request:(config)=>{console.log('Request:',config);returnconfig;},response:(res)=>{console.log('Response:',res);returnres;}});8. 发布类型定义
8.1 package.json 配置
{"name":"my-library","version":"1.0.0","main":"dist/index.js","types":"dist/index.d.ts","files":["dist/**/*.js","dist/**/*.d.ts"],"scripts":{"build":"tsc","prepublishOnly":"npm run build"}}8.2 双包发布
{"name":"my-library","version":"1.0.0","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"},"./utils":{"import":"./dist/utils.mjs","require":"./dist/utils.js","types":"./dist/utils.d.ts"}}}9. 常见模式
9.1 条件类型导出
declaremodule'my-utils'{exportfunctionisString(value:unknown):valueisstring;exportfunctionisNumber(value:unknown):valueisnumber;exportfunctionisArray<T>(value:unknown):valueisT[];}9.2 泛型类型导出
declaremodule'my-utils'{exportfunctionmap<T,U>(array:T[],fn:(item:T,index:number)=>U):U[];exportfunctionfilter<T>(array:T[],predicate:(item:T)=>boolean):T[];exportfunctionreduce<T,U>(array:T[],reducer:(acc:U,item:T)=>U,initialValue:U):U;}9.3 可调用接口
declaremodule'my-utils'{interfaceMyFunction{(value:string):number;version:string;config:{debug:boolean};}constmyFunction:MyFunction;exportdefaultmyFunction;}10. 总结
| 声明类型 | 语法 | 用途 |
|---|---|---|
| 变量 | declare var name: type | 声明全局变量 |
| 函数 | declare function name(params): return | 声明全局函数 |
| 类 | declare class Name { ... } | 声明全局类 |
| 模块 | declare module 'name' { ... } | 声明外部模块 |
| 命名空间 | declare namespace Name { ... } | 声明命名空间 |
| 全局扩展 | declare global { ... } | 扩展全局类型 |
| 通配符 | declare module '*.ext' | 声明文件模块 |