TypeScript 声明文件编写实战:从 8 个示例掌握 .d.ts 的常见 API 模式
文档教程【免费下载链接】TypeScriptTypeScript 使用手册中文版翻译。http://www.typescriptlang.org项目地址https://gitcode.com/gh_mirrors/typ/TypeScript点击查看免费下载导读本文以 TypeScript 使用手册中文版声明文件章节的「举例」篇为核心通过 8 个按复杂度递增组织的真实 API 场景系统讲解如何根据代码库的使用示例书写高质量的.d.ts声明文件。你将掌握declare namespace、declare function、interface、type、declare class、declare var等核心声明语法的适用场景与写法并了解声明文件如何与模块、全局、UMD 等代码库结构协同工作为后续编写和发布自己的类型声明打下坚实基础。为什么从示例出发写声明文件在编写声明文件时我们经常面临这样一种情况手上只有一份 JavaScript 代码库的文档和使用示例而没有现成的类型信息。此时最实际的做法就是依据代码库提供的示例来反推并书写声明文件。TypeScript 使用手册中文版在 zh/declaration-files/introduction.md 中明确指出编写.d.ts文件的常见场景是为某个 npm 包补充类型信息。而「举例」一节即本文的主体原始文档见 zh/declaration-files/by-example.md正是面向还不熟悉 TypeScript 全部语言特性的初学者用一组复杂度递增的例子把「看到一段 API 用法 → 写出对应声明」的完整过程演示出来。在深入每个例子之前先建立一个核心观念TypeScript 中一个名字可能同时承载类型type、**值value和命名空间namespace**三种不同含义。例如class C {}同时创建了指向实例结构的类型C和指向构造函数的值C详见 zh/declaration-files/deep-dive.md 的「简单的组合」一节。声明文件的书写本质就是在为这些含义一一补上类型描述。带属性的对象用declare namespace描述点表示法 API文档场景全局变量myLib包含一个用于创建祝福语的makeGreeting函数以及表示祝福数量的numberOfGreetings属性。代码示例let result myLib.makeGreeting(hello, world); console.log(The computed greeting is: result); let count myLib.numberOfGreetings;声明写法使用declare namespace来描述用点表示法myLib.xxx访问的类型或值。declare namespace myLib { function makeGreeting(s: string): string; let numberOfGreetings: number; }要点讲解namespace在这里起到了容器作用makeGreeting和numberOfGreetings都是挂在全局对象myLib上的成员用命名空间包裹后类型检查器就能正确解析myLib.makeGreeting(...)和myLib.numberOfGreetings。属性numberOfGreetings使用let声明意味着使用者可以读取并修改它如果它只读则应改用const对应declare const如果它在运行时只存在一次也可以用var。命名空间内部既可以放函数、变量也可以放interface、type别名和class。一个典型的全局库模板可参考 zh/declaration-files/templates/global.d.ts.md它展示了如何用declare functiondeclare namespace的组合来描述「既可调用又带属性」的全局库。函数重载declare function的多次声明文档场景getWidget函数接收一个数字参数并返回一个组件或者接收一个字符串参数并返回一个组件数组。代码示例let x: Widget getWidget(43); let arr: Widget[] getWidget(all of them);声明写法为同一个函数名书写多条声明即函数重载。declare function getWidget(n: number): Widget; declare function getWidget(s: string): Widget[];要点讲解declare function用于声明全局函数这里的两个Widget类型需要预先定义可以是interface、class或类型别名声明文件才能完整解析。重载的顺序是有讲究的TypeScript 解析函数调用时会选择第一个匹配到的重载。因此应把具体的重载放在模糊的重载之前例如HTMLDivElement版本先于HTMLElement版本再先于any版本否则后面的具体重载会被前面的模糊重载「隐藏」。这一最佳实践在 zh/declaration-files/do-s-and-don-ts.md 的「函数重载」小节有专门论述与正反例。若能用可选参数或联合类型替代重载应优先使用后者当多个重载仅仅因为末尾参数不同而存在时应改写为可选参数当重载仅因某位置参数类型不同而存在时应改写为联合类型详见同一文档中的「使用可选参数」「使用联合类型」小节。可重用类型接口用interface定义对象形状文档场景当指定一个祝福词时必须传入一个GreetingSettings对象它具有以下属性greeting必需的字符串duration可选的持续时间以毫秒表示color可选的字符串比如#ff00ff。代码示例greet({ greeting: hello world, duration: 4000, });声明写法使用interface定义一个带有属性的类型。interface GreetingSettings { greeting: string; duration?: number; color?: string; } declare function greet(setting: GreetingSettings): void;要点讲解属性名后的?表示该属性为可选可选属性在调用时允许省略但当对象字面量显式提供了该属性时其值类型仍会被严格检查。interface与declare function配合参数类型指向接口调用方传入的对象只要结构匹配即可无需强制使用某个具名类型变量。从声明文件的组合能力看interface还支持声明合并——同一名字的多个interface声明会合并成员interface Foo { x: number }与另一处的interface Foo { y: number }合并后同时拥有x与y。这是 zh/declaration-files/deep-dive.md 中「高级组合」一节给出的重要技巧可用于为已有库的类型渐进式补充成员。需要注意的是不能通过interface向类型别名type s string添加成员。可重用类型类型别名用type定义联合与短名文档场景在任何需要祝福词的地方你可以提供一个string、一个返回string的函数或一个Greeter实例。代码示例function getGreeting() { return howdy; } class MyGreeter extends Greeter {} greet(hello); greet(getGreeting); greet(new MyGreeter());声明写法使用类型别名定义类型的短名。type GreetingLike string | (() string) | MyGreeter; declare function greet(g: GreetingLike): void;要点讲解类型别名把「字符串 | 返回字符串的函数 | Greeter 实例」三种形态折叠成一个名字GreetingLike调用方传入三种值中的任意一种都能通过类型检查。MyGreeter是Greeter的子类因为声明文件只描述结构structural typing子类实例天然满足父类类型的约束因此greet(new MyGreeter())合法。函数类型的写法() string是 TypeScript 的函数类型字面量注意与函数声明的区分当参数也需要类型时可写为(s: string) string。在声明文件里type和interface的选用没有绝对规则interface更适合描述对象形状且支持声明合并type更擅长表达联合类型、元组和映射类型等复合形态。本仓库的 zh/declaration-files/templates/global.d.ts.md 模板中同时出现了interface CatSettings与type VetID string | number正体现了二者的分工。组织类型用命名空间把相关类型分组文档场景greeter对象能够记录到文件或显示一个警告。你可以为.log(...)提供 log 选项并为.alert(...)提供 alert 选项。代码示例const g new Greeter(Hello); g.log({ verbose: true }); g.alert({ modal: false, title: Current Greeting });声明写法使用命名空间组织类型。declare namespace GreetingLib { interface LogOptions { verbose?: boolean; } interface AlertOptions { modal: boolean; title?: string; color?: string; } }你也可以在一个声明中创建嵌套的命名空间declare namespace GreetingLib.Options { // Refer to via GreetingLib.Options.Log interface Log { verbose?: boolean; } interface Alert { modal: boolean; title?: string; color?: string; } }要点讲解命名空间的价值在于防止全局命名冲突把LogOptions、AlertOptions收进GreetingLib命名空间后多个声明文件共存也不会互相污染全局作用域。这与 zh/declaration-files/library-structures.md 脚注「防止命名冲突」的建议一致——用代码库提供的全局变量来承载命名空间而不是在顶层直接铺开CatsKittySettings之类的扁平类型名。嵌套命名空间GreetingLib.Options提供更深的组织层次使用时通过GreetingLib.Options.Log引用。不过要留意命名空间层级越深使用者的书写成本越高应权衡 API 的直观性。从底层机制看zh/declaration-files/deep-dive.md类型可以存在于命名空间中例如let x: A.B.C中的C类型来自A.B命名空间——这里A.B本身不必是类型或值这正解释了为何命名空间可以作为类型的「逻辑分组」而非运行时结构。类用declare class描述可实例化对象文档场景你可以通过实例化Greeter对象来创建祝福语或者继承Greeter对象来自定义祝福语。代码示例const myGreeter new Greeter(hello, world); myGreeter.greeting howdy; myGreeter.showGreeting(); class SpecialGreeter extends Greeter { constructor() { super(Very special greetings); } }声明写法使用declare class来描述一个类或像类一样的对象。类可以有属性和方法就和构造函数一样。declare class Greeter { constructor(greeting: string); greeting: string; showGreeting(): void; }要点讲解declare class里同时声明了构造函数签名constructor(greeting: string)、实例属性greeting: string可读写和实例方法showGreeting(): void。因为声明中包含了构造签名new Greeter(hello, world)是合法的同时类声明允许被继承因此示例中的class SpecialGreeter extends Greeter能被正确解析子类构造器里的super(Very special greetings)也会按父类构造签名校验。类声明在 TypeScript 中同时产生「类型」和「值」两个含义zh/declaration-files/deep-dive.md 的「内置组合」一节类型Greeter指向实例结构值Greeter指向构造函数。声明文件里声明类时这两层含义都会被携带。若某模块把「类构造函数」作为模块的导出对象例如const Greeter require(super-greeter)后new Greeter()使用应使用export MyClass加declare class MyClass的组合参考 zh/declaration-files/templates/module-class.d.ts.md 模板。全局变量用declare var/declare const/declare let文档场景全局变量foo包含了存在的组件总数。代码示例console.log(Half the number of widgets is foo / 2);声明写法使用declare var声明变量。如果变量是只读的那么可以使用declare const。你还可以使用declare let如果变量拥有块级作用域。/** The number of widgets present */ declare var foo: number;要点讲解三种全局变量声明的语义差异在于可变性与作用域declare var允许读写且遵循var的规则declare const表示只读常量declare let表示可写但拥有块级作用域。选择哪种取决于全局变量在运行时实际的行为。声明文件中的变量也可以带 JSDoc 风格注释如示例中的/** The number of widgets present */这有助于生成更好的编辑器悬停提示。注意 zh/declaration-files/do-s-and-don-ts.md 中的提醒不要使用封箱后的Number、String、Boolean、Symbol或Object作为类型应使用小写原始类型number、string、boolean、symbol需要「任意对象」时用非原始的object该建议在 zh/release-notes/typescript-2.2.md 中提及。全局函数用declare function文档场景你可以使用一个字符串参数来调用greet函数并向用户显示一条祝福语。代码示例greet(hello, world);声明写法使用declare function来声明函数。declare function greet(greeting: string): void;要点讲解这是最简单的声明形态函数名 参数列表 返回类型。void表示调用者不应依赖其返回值。当全局函数同时需要附带属性时既可作为函数调用又挂有属性可把declare function与declare namespace组合使用——这是 zh/declaration-files/templates/global.d.ts.md 中myLib模板展示的经典结构。如果某个模块的导出本身就是一个可调用函数如const x require(foo); const y x(42);则应使用 zh/declaration-files/templates/module-function.d.ts.md 模板其核心是export MyFunction配合多个declare function重载并把返回类型等组织进同名namespace MyFunction。综合从示例到完整声明文件的完整流程把 8 个示例串起来可以得到一条可复用的工作流收集 API 使用示例阅读代码库文档摘录所有典型调用方式函数调用、new实例化、属性访问、对象字面量参数等。判断代码库形态区分模块化代码库、全局代码库与 UMD 代码库这决定了声明文件的顶层结构export还是declare全局。识别方法见 zh/declaration-files/library-structures.md模块化代码库通常有无条件require/define、import/export语句或对exports/module.exports的赋值全局代码库则有顶层var/function或window.someName赋值同时存在typeof define、typeof window、typeof module检测的则是 UMD。逐条匹配声明语法函数调用 →declare function重载按具体在前排序对象参数 →interface多形态参数 →type联合别名点表示法成员 →declare namespace可实例化/可继承 →declare class全局变量 →declare var/const/let。组织与检查用命名空间避免全局冲突按 zh/declaration-files/do-s-and-don-ts.md 的规范检查常见错误如避免any、回调返回值用void、重载顺序、优先可选参数与联合类型。对照模板快速起步zh/declaration-files/templates.md 提供了 7 个现成模板module.d.ts、module-class.d.ts、module-function.d.ts、module-plugin.d.ts、global.d.ts、global-plugin.d.ts、global-modifying-module.d.ts直接基于最贴近库形态的模板改写可大幅减少遗漏。结语与延伸阅读「举例」篇以 8 个递进示例覆盖了声明文件最常用的声明语法。要写出生产级的声明文件还需要掌握更多维度理解类型、值与命名空间的三重含义及声明合并zh/declaration-files/deep-dive.md、识别代码库结构与依赖方式zh/declaration-files/library-structures.md、遵循最佳实践规避常见错误zh/declaration-files/do-s-and-don-ts.md、使用模板快速起步zh/declaration-files/templates.md以及如何将声明文件发布为 npm 包zh/declaration-files/publishing.md与查找安装现成的类型包zh/declaration-files/consumption.md。若你正在为某个 npm 包补类型可直接从 zh/declaration-files/templates/module.d.ts.md 模板开始。赞分享文档教程【免费下载链接】TypeScriptTypeScript 使用手册中文版翻译。http://www.typescriptlang.org项目地址https://gitcode.com/gh_mirrors/typ/TypeScript点击查看免费下载相关推荐Satellite Eyes用户指南从基础设置到高级自定义的完整教程Satellite Eyes用户指南从基础设置到高级自定义的完整教程 Satellite Eyes是一款专为Mac OS X打造的实用工具能够自动将您的桌面React Redux TypeScript 声明文件终极指南从零掌握自定义.d.ts文件编写技巧React Redux TypeScript 声明文件终极指南从零掌握自定义.d.ts文件编写技巧 在React与Redux应用开发中TypeScript的前端教程TypeScript声明文件编写d.ts文件创建与维护指南TypeScript声明文件编写d.ts文件创建与维护指南 TypeScript声明文件.d.ts是连接JavaScript世界与TypeScript类型教程上一篇旧iPad降级提速保姆级指南用Legacy-iOS-Kit一键把老设备刷回轻快系统下一篇Prettier 3.x TypeScript保持 as / satisfies 独占行类型前的 own-line 注释位置19939创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →