文档教程【免费下载链接】TypeScriptTypeScript 使用手册中文版翻译。http://www.typescriptlang.org项目地址https://gitcode.com/gh_mirrors/typ/TypeScript点击查看免费下载本文是 TypeScript 使用手册中文版声明文件·模板系列的技术指南围绕全局代码库Global Library这一库形态讲解如何识别它、如何为它编写global.d.ts声明文件并逐段剖析官方模板的每个声明构造。读完本文你将能独立判断一个 JavaScript 库是否属于全局代码库、在动手前排除 UMD 陷阱并依据模板写出类型安全、可发布、可复用的全局声明文件。什么是全局代码库全局代码库是指可以通过全局作用域直接访问的代码库——即不使用任何形式的import语句就能使用它。许多代码库只是简单地导出一个或多个供使用的全局变量。例如如果你使用 jQuery那么可以直接通过$变量来引用它无需任何导入语句$(() { console.log(hello!); });从使用方式上看你通常能够在代码库的文档里看到如何在 HTML 的script标签里引用它script srchttp://a.great.cdn.for/someLib.js/scriptscript标签将代码加载进页面后其中定义的全局变量如上面的$即可在后续所有脚本中直接访问。这正是全局代码库与模块化代码库最根本的使用差异前者依赖浏览器全局作用域后者依赖模块加载器。在动手编写声明文件之前请务必确认一点目前大多数流行的全局代码库都以 UMD 代码库的形式发布而 UMD 代码库与全局代码库很难通过文档来区分。因此编写全局代码库的声明文件之前必须先确认目标代码库不是UMD 代码库UMD 的判断方法与模板见后文。从代码识别全局代码库通常全局代码库的源码十分简单。一个全局的 Hello, world 代码库可以是这样function createGreeting(s) { return Hello, s; }或者这样window.createGreeting function (s) { return Hello, s; };在阅读全局代码库的代码时你会看到以下特征顶层的var语句或function声明一个或多个window.someName赋值语句假设 DOM 相关的原始值document或window存在即代码直接使用它们而非通过导入获得。而下面这些特征不会出现在真正的全局代码库中检查或使用了模块加载器如require或defineCommonJS / Node.js 风格的导入语句如var fs require(fs);define(...)调用描述require或导入代码库的文档。这些正反特征是判断代码库形态的速查表正面命中顶层声明、window赋值、DOM 依赖反面完全没有模块加载器痕迹那么它就是一个全局代码库。相关内容在《代码库结构》一节中也有完整的对应说明可作为交叉验证。一个重要的反例UMD 代码库UMD 模块既可以用作 ES 模块使用导入语句也可以用作全局变量在缺少模块加载器的环境中使用。识别它的关键是源码顶端的环境探测代码(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) {如果你看到代码库中存在类似typeof define、typeof window或typeof module的检测代码——尤其是在文件顶端——那么它大概率是 UMD 代码库此时应改用模块模板或 module-plugin 模板而不是全局代码库模板。全局代码库的现状由于将全局代码库转换为 UMD 代码库十分容易如今很少有代码库仍然保持全局代码库风格。不过小型的代码库以及需要使用 DOM 的代码库仍然可以是全局的——例如直接操作document、window的工具库或通过 CDNscript引入的轻量插件。这类库正是global.d.ts模板的适用对象。全局代码库模板逐段解析模板文件 global.d.ts 以myLib为例完整展示了为全局代码库编写声明文件的骨架。下面逐段解析每一部分的作用并给出可直接套用、可复制的完整模板// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~] // Project: [~THE PROJECT NAME~] // Definitions by: [~YOUR NAME~] [~A URL FOR YOU~] /*~ If this library is callable (e.g. can be invoked as myLib(3)), *~ include those call signatures here. *~ Otherwise, delete this section. */ declare function myLib(a: string): string; declare function myLib(a: number): number; /*~ If you want the name of this library to be a valid type name, *~ you can do so here. *~ *~ For example, this allows us to write var x: myLib; *~ Be sure this actually makes sense! If it doesnt, just *~ delete this declaration and add types inside the namespace below. */ interface myLib { name: string; length: number; extras?: string[]; } /*~ If your library has properties exposed on a global variable, *~ place them here. *~ You should also place types (interfaces and type alias) here. */ declare namespace myLib { //~ We can write myLib.timeout 50; let timeout: number; //~ We can access myLib.version, but not change it const version: string; //~ Theres some class we can create via let c new myLib.Cat(42) //~ Or reference e.g. function f(c: myLib.Cat) { ... } class Cat { constructor(n: number); //~ We can read c.age from a Cat instance readonly age: number; //~ We can invoke c.purr() from a Cat instance purr(): void; } //~ We can declare a variable as //~ var s: myLib.CatSettings { weight: 5, name: Maru }; interface CatSettings { weight: number; name: string; tailLength?: number; } //~ We can write const v: myLib.VetID 42; //~ or const v: myLib.VetID bob; type VetID string | number; //~ We can invoke myLib.checkCat(c) or myLib.checkCat(c, v); function checkCat(c: Cat, s?: VetID); }头部注释与占位符模板前三行是声明文件的元信息注释其中的[~THE LIBRARY NAME~]、[~THE PROJECT NAME~]、[~YOUR NAME~] [~A URL FOR YOU~]均为占位符分别应替换为声明文件所描述代码库的名称可附版本号、代码库所属项目名称、维护者姓名与可联系的 URL。这与《发布》一节中提到的types包维护约定一致。可调用库declare function重载declare function myLib(a: string): string; declare function myLib(a: number): number;如果该库可以被直接调用例如myLib(3)就在这里声明它的调用签名。注意模板使用了两条重载分别处理string与number两种入参——这是声明文件中对函数重载的标准表达方式与《举例》一节中getWidget的重载示例完全一致。如果库不可调用请删除这一节。库名作为类型名interfaceinterface myLib { name: string; length: number; extras?: string[]; }如果希望myLib这个名字本身可以充当类型名例如允许写var x: myLib可以在此声明一个同名接口。模板注释提醒请确保这样声明确实有意义——只有当库的真实 API 确实是一个具有这些属性的对象时才保留它否则应删除该声明并把需要的类型放进下面的命名空间里。全局变量的属性与类型declare namespacedeclare namespace myLib { let timeout: number; const version: string; // ...class / interface / type / function... }如果代码库在全局变量上暴露了属性例如myLib.timeout、myLib.version就将它们放进declare namespace中。这是全局代码库声明文件的核心组织方式命名空间既承载值let/const/class/function也承载类型interface/type使用者通过点号访问例如myLib.Cat、myLib.VetID。模板中各个声明成员的语义逐一解读如下声明使用效果说明let timeout: number;可以写myLib.timeout 50;属性可读写const version: string;可以读myLib.version但不能修改属性只读class Catlet c new myLib.Cat(42)function f(c: myLib.Cat)构造函数 实例成员readonly age: number只读属性、purr(): void方法interface CatSettingsvar s: myLib.CatSettings { weight: 5, name: Maru };含可选属性tailLength?: numbertype VetID string \| number;const v: myLib.VetID 42;或 bob;联合类型别名function checkCat(c: Cat, s?: VetID);myLib.checkCat(c)或myLib.checkCat(c, v)可选参数命名空间背后的类型/值/命名空间三义性从《深入》一节的原理看declare namespace myLib之所以能同时容纳值timeout、Cat构造函数与类型CatSettings、VetID是因为 TypeScript 中一个名字可以承载三种不同的意义类型、值、命名空间。例如class Cat同时创建了类型Cat指向实例结构与值Cat指向构造函数二者名字相同却不冲突。这正是全局代码库声明文件能在一个myLib名下完整描述复杂 API 的底层机制。防止命名冲突把类型放进命名空间在全局作用域里虽然可以定义许多类型但强烈不建议这样做——当一个工程中存在多个声明文件时全局顶层类型很容易导致难以解决的命名冲突。这一点在《代码库结构》的脚注防止命名冲突中有明确告诫。可以遵循的简单规则是使用代码库提供的某个全局变量来声明拥有命名空间的类型。例如如果代码库提供了全局变量cats应该这样写declare namespace cats { interface KittySettings {} }而不是在全局顶层这样写// at top-level interface CatsKittySettings {}这样组织的好处是保证代码库将来可以被转换为 UMD 模块且不会影响声明文件的使用者——所有类型都收纳在cats.前缀之下命名空间天然隔离了冲突。全局声明文件中的依赖处理你的全局代码库声明文件可能还会依赖其他代码库。依据《代码库结构》的利用依赖一节依赖声明方式取决于依赖方的形态依赖某个全局代码库使用/// reference types... /指令/// reference typessomeLib / function getThing(): someLib.thing;依赖某个模块使用import语句注意模块导入会改变文件的全局性质需结合实际情况权衡import * as moment from moment; function getThing(): moment;全局代码库依赖某个 UMD 模块同样使用/// reference typesmoment /指令随后即可在全局声明中引用moment暴露出的类型。反之模块或 UMD 代码库依赖 UMD 代码库时应使用import语句此时不要使用/// reference指令。此外发布声明文件时详见《发布》不要在声明文件里使用/// reference path... /文件路径引用而应使用/// reference types... /包名引用并确保被依赖的声明包写在package.json的dependencies而非devDependencies中以便使用你声明文件的下游用户自动获得这些依赖。与其他模板的边界global、plugin 与 modifying-module全局代码库模板只是《模板》七件套之一。为选对模板需要区分三种容易混淆的全局相关形态模板适用对象关键差异global.d.ts全局代码库本身通过全局作用域使用无import参与global-plugin.d.ts全局插件一段全局代码修改全局对象结构如给String.prototype、Array.prototype添加方法global-modifying-module.d.ts修改全局作用域的模块需要require/import激活导入时修改全局作用域如给String.prototype添加成员模板中使用declare global { ... }块全局插件与修改全局作用域的模块的文档示例通常是这样的——给内置类型添加新方法var x hello, world; // Creates new methods on built-in types console.log(x.startsWithHello()); var y [1, 2, 3]; // Creates new methods on built-in types console.log(y.reverseAndSort());二者的区别在于激活方式全局插件通过script加载即生效修改全局作用域的模块则需要一行不接收返回值的require(magic-string-time)来激活副作用。这种模式存在运行时冲突的风险但在明确的前提下仍可为其编写声明文件——通过declare global将声明注入全局命名空间其底层机制与《声明合并》中全局扩展一节描述的行为一致在模块内部用declare global { interface ArrayT { ... } }扩充内置类型扩展内容与原始声明合并但不能声明新的顶层声明只能扩展已存在的声明。与之相对如果库既是模块又希望暴露全局变量名则属于 UMD 形态应在module.d.ts 模板中使用export as namespace myLib;来声明全局名而不是使用全局代码库模板。从模板到真实项目发布与使用声明文件写好后有两条发布路径详见《发布》与 npm 包捆绑发布在package.json中通过types字段指向声明文件typings与types意义相同若主声明文件恰好是包根目录下的index.d.ts与index.js并列则无需显式指定。提交到 DefinitelyTyped由社区统一发布为types/xxx包使用者通过npm install --save types/xxx安装详见《使用》。对使用者而言安装好声明文件后无论是通过import导入还是直接在全局作用域使用_TypeScript 都能获得完整的类型提示与编译期检查。小结global.d.ts模板解决的核心问题是在无模块系统的场景下如何为通过全局作用域暴露的代码库提供完整的类型描述。本文覆盖了全局代码库的定义与识别顶层声明、window赋值、DOM 依赖为正特征require/define/模块加载器为反特征、UMD 陷阱的排除、模板中declare function可调用、interface类型名、declare namespace属性与类型收纳三大构造的逐段用法以及命名冲突规避、依赖声明、模板边界判定与发布消费链路。建议结合《代码库结构》的库形态全景和《举例》的 API 模式示例一起阅读形成从识别库类型到写出声明文件的完整方法论。赞分享文档教程【免费下载链接】TypeScriptTypeScript 使用手册中文版翻译。http://www.typescriptlang.org项目地址https://gitcode.com/gh_mirrors/typ/TypeScript点击查看免费下载相关推荐企业级客服机器人语义理解实践指南如何利用GTE-large-zh提升智能客服效果企业级客服机器人语义理解实践指南如何利用GTE large zh提升智能客服效果 在当今数字化时代 GTE large zh 作为阿里巴巴达摩院开发的高性能文档教程QtScrcpy3分钟实现安卓设备跨平台高清投屏控制QtScrcpy3分钟实现安卓设备跨平台高清投屏控制 QtScrcpy是一款基于Qt框架开发的Android实时显示控制软件通过USB或网络连接无需roo文档教程Gatsby Monorepo 全局 TypeScript 声明注入机制解析以 types/gatsby-monorepo 的 global.d.ts 为例Gatsby Monorepo 全局 TypeScript 声明注入机制解析以 types/gatsby monorepo 的 global.d.ts 为例前端静态站点Web框架上一篇视频转文字不再难Bili2text让B站内容轻松变成可编辑文本下一篇leetcode-rust项目部署指南如何搭建个人算法练习平台创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考