TypeScript 声明文件模块模板 module.d.ts:为模块化代码库编写类型定义的完整指南
文档教程【免费下载链接】TypeScriptTypeScript 使用手册中文版翻译。http://www.typescriptlang.org项目地址https://gitcode.com/gh_mirrors/typ/TypeScript点击查看免费下载在 TypeScript 生态中为 JavaScript 库编写.d.ts声明文件是让库获得完整类型提示与静态检查能力的关键环节。本文围绕 TypeScript 使用手册中文版中的 module.d.ts 模板 展开系统讲解该模板的每一处结构、适用场景与背后的 TypeScript 类型系统原理。阅读本文后你将能够识别模块化代码库理解声明文件中值、类型、命名空间三种含义的组合方式并根据本模板为任意普通模块编写可发布、可维护的index.d.ts声明文件。模板定位何时使用 module.d.ts在开始逐段拆解之前先明确该模板在整套声明文件模板中的位置。TypeScript 声明文件按其服务对象分为全局库与模块化库两大类其中模块化代码库如绝大多数 Node.js 库、ES 模块库有四个官方模板可供选择module.d.ts——模块既不可调用、也不可构造时使用即模块导出的是普通值、函数、类型与命名空间的组合module-function.d.ts——模块本身可作为函数调用如const y x(42)module-class.d.ts——模块可用new构造如const y new x(hello)module-plugin.d.ts——模块在导入后会修改其他模块插件模式。正如 library-structures.md 所强调的你应该先阅读module.d.ts以便从整体上了解它们的工作方式。因此本模板是理解其他所有模块类模板的基石。如何从源码识别一个库是否属于模块化代码库library-structures.md 给出了可操作的特征模块化代码库至少包含以下代表性条目之一——无条件的require或define调用、import * as a from b;或export c;这样的声明、对exports或module.exports的赋值它们极少包含对window或global的赋值。凡是符合这些特征、只能运行在模块加载器环境中的库如必须用 CommonJSrequire加载的express其声明文件都应以本模板为起点。模板逐段拆解module.d.ts模板本体是一个带详细注释的 TypeScript 代码文件注释以/*~标记开头方便你在复制后快速定位需要修改或删除的段落。下面按结构逐段说明其含义与处理方式。1. 文件头库信息与归属声明// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~] // Project: [~THE PROJECT NAME~] // Definitions by: [~YOUR NAME~] [~A URL FOR YOU~]前三行是声明文件的元信息注释分别填写库的名称与可选版本号、所属项目名称以及维护者姓名与主页地址。这部分约定俗成的头部信息不仅用于人类阅读也便于社区工具与搜索引擎识别声明文件归属。2. 命名与放置index.d.ts 规则/*~ This is the module template file. You should rename it to index.d.ts *~ and place it in a folder with the same name as the module. *~ For example, if you were writing a file for super-greeter, this *~ file should be super-greeter/index.d.ts */模板明确指出必须将文件重命名为index.d.ts并放在与模块同名的文件夹中。例如为super-greeter库编写声明就应创建super-greeter/index.d.ts。这与 TypeScript 的模块解析规则完全一致——与 JavaScript 源码结构中index.js的作用一样index.d.ts是解析import ... from super-greeter时定位到的声明入口。对于发布到 npm 的库该文件最终会落入types/包如types/super-greeter或随库源码一起发布。若库本身含有多级子模块结构global-plugin.d.ts 模板 的代码库文件结构一节展示了myLib/foo、myLib/bar/baz对应的声明文件需按同样目录层级铺设foo.d.ts、bar/index.d.ts、bar/baz.d.ts的做法——即声明文件结构应反映源码结构。3. UMD 全局变量声明export as namespace/*~ If this module is a UMD module that exposes a global variable myLib when *~ loaded outside a module loader environment, declare that global here. *~ Otherwise, delete this declaration. */ export as namespace myLib;export as namespace myLib声明该模块在缺少模块加载器的环境如直接通过script标签引入中会暴露一个名为myLib的全局变量。这是 UMD 模块的典型特征它既可以用import/require按模块方式使用又可以在浏览器全局作用域中直接访问。需要注意的关键判断只有当你的库确实是 UMD 模块时才保留此行否则应删除。library-structures.md 给出了识别 UMD 库的方法——查看源码文件顶端是否存在typeof define、typeof window或typeof module之类的环境检测代码(function (root, factory) { if (typeof define function define.amd) { define([libName], factory); } else if (typeof module object module.exports) { module.exports factory(require(libName)); } else { root.returnExports factory(root.libName); } }(this, function (b) {大多数流行的库jQuery、Moment.js、lodash都提供 UMD 格式包。若你的库不是 UMD却保留export as namespace会给使用者带来误导性的全局变量声明。4. 模块方法函数导出/*~ If this module has methods, declare them as functions like so. */ export function myMethod(a: string): string; export function myOtherMethod(a: number): number;对于模块对外暴露的函数方法直接使用export function声明。此处需要注意 TypeScript 声明文件的写法函数体不需要实现只保留签名参数类型与返回类型。模板给出了两个示例myMethod(a: string): string——接收字符串、返回字符串myOtherMethod(a: number): number——接收数字、返回数字。当函数存在多种参数形态时可以像 module-function.d.ts 中那样声明多个重载签名TypeScript 会按声明顺序匹配调用declare function MyFunction(name: string): MyFunction.NamedReturnType; declare function MyFunction(length: number): MyFunction.LengthReturnType;5. 模块类型接口导出/*~ You can declare types that are available via importing the module */ export interface someType { name: string; length: number; extras?: string[]; }通过export interface导出的类型可以被模块使用者直接导入并使用。模板示例定义了一个someType接口包含必填的name: string与length: number以及可选的extras?: string[]?表示该属性可省略。使用者可以这样写import { someType } from yourModule; const item: someType { name: demo, length: 3 };在模块声明文件中接口、类型别名、枚举、类等都可以通过export暴露给外部这是模块类模板与全局模板通过declare namespace暴露最直观的差异。6. 模块属性值导出/*~ You can declare properties of the module using const, let, or var */ export const myField: number;模块自身除了方法和类型还可以携带属性值。模板示范了用const声明一个只读属性myField: number。若属性需要可修改则可改用let或var。这一声明对应着模块的顶层导出对象例如import { myField } from yourModule; console.log(myField); // number7. 子命名空间export namespace的使用/*~ If there are types, properties, or methods inside dotted names *~ of the module, declare them inside a namespace. */ export namespace subProp { /*~ For example, given this definition, someone could write: *~ import { subProp } from yourModule; *~ subProp.foo(); *~ or *~ import * as yourMod from yourModule; *~ yourMod.subProp.foo(); */ export function foo(): void; }这是模板中最体现 TypeScript 类型系统精髓的部分当模块暴露带点号路径的嵌套结构dotted names时需要把这些成员放进namespace声明中。示例中的subProp命名空间导出函数foo(): void使用者有两种访问方式// 方式一具名导入子命名空间 import { subProp } from yourModule; subProp.foo(); // 方式二整体导入模块对象 import * as yourMod from yourModule; yourMod.subProp.foo();命名空间在这里同时充当类型容器与值容器subProp作为值时承载foo函数而命名空间内部的接口、类型别名则可以通过yourModule.subProp.SomeType的方式在类型位置使用。这与 deep-dive.md 中阐述的核心概念一脉相承。理解模板背后的类型系统值、类型与命名空间module.d.ts 模板之所以能同时容纳函数、接口、常量与命名空间是因为 TypeScript 声明文件中的名字可以有三种不同含义deep-dive.md 对此有系统论述类型通过type别名、interface、class、enum或指向类型的import创建只能出现在类型位置值通过let/const/var、包含值的namespace、enum、class、function或指向值的import创建能出现在表达式位置命名空间用于限定类型归属如let x: A.B.C中的C来自A.B命名空间。一个名字可以同时承载多种含义。class C { }就同时创建了类型C实例结构与值C构造函数enum也有相似行为。在声明文件中我们可以通过组合让一个名字既表示值又表示类型。例如 deep-dive.md 中的示例// foo.d.ts export var Bar: { a: Bar }; export interface Bar { count: number; }使用时即可解构并同时利用两种含义import { Bar } from ./foo; let x: Bar Bar.a; // 左侧是类型右侧是值 console.log(x.count);这正是module.d.ts模板把export function、export interface、export const、export namespace并列排布的根本原因——它们分别对应模块导出对象上的值成员、类型成员与嵌套命名空间组合起来才能完整刻画真实库的 API 形态。声明文件中的依赖处理实际库往往依赖其他库在编写模块声明文件时需要正确处理依赖。根据 library-structures.md 与 global-plugin.d.ts 模板 的利用依赖一节规则如下依赖全局库使用/// reference typessomeLib /指令依赖普通模块在声明文件顶部使用import语句如import * as moment from moment;模块/UMD 库依赖 UMD 库同样使用import语句不要用/// reference指令声明对 UMD 库的依赖。一个融合了导入依赖与导出声明的index.d.ts示例/// reference typessomeGlobalLib / import * as moment from moment; export function formatTime(t: moment.Moment): string; export interface Options { locale?: string; precision: number; }实战按模板编写一个完整声明文件下面以手册中反复出现的super-greeter为例将模板各段落到一个具体的普通模块场景中。假设库的 JavaScript 用法是var greeter require(super-greeter); greeter.greet(World); greeter.LEVEL; // 模块属性 var opts greeter.makeOptions(); // 返回配置对象则super-greeter/index.d.ts可以按模板组织为// Type definitions for super-greeter 2.3.0 // Project: https://example.com/super-greeter // Definitions by: Jane Doe https://example.com/jane /*~ 非 UMD 模块删除 export as namespace 段 */ /*~ 模块方法 */ export function greet(name: string): string; /*~ 模块属性只读 */ export const LEVEL: number; /*~ 模块暴露的类型 */ export interface GreetingOptions { language?: string; emoji?: boolean; } /*~ 返回配置对象的方法嵌套类型放入 namespace */ export function makeOptions(): superGreeter.Options; export namespace superGreeter { export interface Options { level: number; prefix?: string; } }使用者即可获得完整类型支持import * as greeter from super-greeter; const msg: string greeter.greet(World); const opts: greeter.superGreeter.Options greeter.makeOptions();与其他模块模板的衔接本模板描述的是普通模块形态。当模块的行为超出导出普通值/类型时需要切换到对应的专门模板模块可被当作函数调用const y x(42)→ 使用 module-function.d.ts。该模板用export MyFunction导出函数对象并用declare namespace MyFunction挂载返回类型与属性模块可用new构造const y new x(hello)→ 使用 module-class.d.ts。该模板用export MyClass导出类构造函数模块导入后会修改其他模块→ 使用 module-plugin.d.ts。该模板先import * as m from someModule再declare module someModule扩充原模块。需要注意module-function.d.ts与module-class.d.ts都采用了export 导出整个对象而非具名导出其模板注释特别提醒ES6 模块无法直接导出可调用的类对象此类文件应按 CommonJS 风格import x require([~THE MODULE~])导入或者在开启--allowSyntheticDefaultImports或--esModuleInterop编译选项后使用默认导入import x from [~THE MODULE~]。library-structures.md 的脚注进一步说明ES6 模块加载器中顶层对象只能拥有属性、永远不能被调用常见的解决手段是为可调用的对象定义default导出若在tsconfig.json中启用了esModuleInterop: trueTypeScript 会自动处理这一差异。相比之下本模板使用标准具名导出与 ES 模块互操作更直接。常见误区与最佳实践结合手册 deep-dive.md 与其他模板使用module.d.ts时有几点值得注意删除不匹配的段落非 UMD 模块务必删除export as namespace模块没有嵌套成员时删除export namespace段。模板中的/*~注释块正是为此设计逐段对照实际库的 API 决定保留或删除。命名空间内使用export模块模板中的命名空间成员必须显式export否则外部无法访问这与全局模板declare namespace的内部语义一致。类型与值的同名组合可以利用接口与变量同名声明让使用方一次导入同时获得类型与值但要注意两者是独立的声明互不约束。避免顶层全局类型library-structures.md 的防止命名冲突脚注建议类型应放在模块/命名空间内部而不是散落在全局作用域以免多声明文件工程中出现难以解决的命名冲突。小结module.d.ts是 TypeScript 声明文件模板体系中对普通模块最通用的一张模板它以函数 接口 常量 命名空间四种导出形态覆盖了绝大多数非可调用、非可构造模块的 API 刻画需求同时也是理解module-function.d.ts、module-class.d.ts、module-plugin.d.ts的基础。编写时只需记住三条主线识别库是否为模块化代码库 → 按模板逐段填写并删除不匹配段落 → 处理好依赖导入即可产出结构规范、可发布的声明文件。相关模板全家桶可在 templates 目录 中按需查阅模块与全局库的完整识别方法详见 library-structures.md类型与值组合的深入原理可参考 deep-dive.md。赞分享文档教程【免费下载链接】TypeScriptTypeScript 使用手册中文版翻译。http://www.typescriptlang.org项目地址https://gitcode.com/gh_mirrors/typ/TypeScript点击查看免费下载相关推荐企业级客服机器人语义理解实践指南如何利用GTE-large-zh提升智能客服效果企业级客服机器人语义理解实践指南如何利用GTE large zh提升智能客服效果 在当今数字化时代 GTE large zh 作为阿里巴巴达摩院开发的高性能文档教程OrcaSlicer 自适应床网快速指南只探测打印区域告别整床调平OrcaSlicer 自适应床网快速指南只探测打印区域告别整床调平 只打印一个 30mm 的小零件却要为整张 220×220 的床探测二十几个点Orca文档教程TypeScript 模块插件声明文件编写指南module-plugin.d.ts 模板深度解析TypeScript 模块插件声明文件编写指南module plugin.d.ts 模板深度解析 本指南围绕 TypeScript 中文手册声明文件章节中的文档教程上一篇MiniRust 开源项目教程下一篇CAI 多 Agent 交接提示词扩展解析用 RECOMMENDED_PROMPT_PREFIX 让 Handoff 协作更稳定创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考