按照本指南的步骤编写声明文件后,现在是将其发布到 npm 的时候了。有两种主要方法可以将声明文件发布到 npm
- 与 npm 包捆绑在一起
- 发布到 npm 上的 @types 组织。
如果你的类型是由源代码生成的,请使用源代码发布类型。TypeScript 和 JavaScript 项目都可以通过 declaration
生成类型。
否则,我们建议将类型提交到 DefinitelyTyped,它会将类型发布到 npm 上的 @types
组织。
在 npm 包中包含声明
如果你的包有一个主 .js
文件,你还需要在 package.json
文件中指明主声明文件。将 types
属性设置为指向你的捆绑声明文件。例如
json
{"name": "awesome","author": "Vandelay Industries","version": "1.0.0","main": "./lib/main.js","types": "./lib/main.d.ts"}
请注意,"typings"
字段与 types
同义,也可以使用。
依赖项
所有依赖项都由 npm 管理。确保你依赖的所有声明包都已在 package.json
中的 "dependencies"
部分中做了适当标记。例如,假设我们编写了一个使用 Browserify 和 TypeScript 的包。
json
{"name": "browserify-typescript-extension","author": "Vandelay Industries","version": "1.0.0","main": "./lib/main.js","types": "./lib/main.d.ts","dependencies": {"browserify": "latest","@types/browserify": "latest","typescript": "next"}}
在此,我们的软件包依赖于browserify
和typescript
软件包。browserify
不会将其声明文件与其 npm 软件包捆绑在一起,因此我们需要依赖@types/browserify
来进行声明。另一方面,typescript
会打包其声明文件,因此无需任何其他依赖项。
我们的软件包公开每个软件包的声明,因此我们browserify-typescript-extension
软件包的任何用户都需要同时具有这些依赖项。出于这个原因,我们使用了"dependencies"
而不是"devDependencies"
,否则我们的使用者将需要手动安装这些软件包。如果我们刚刚编写了一个命令行应用程序,并且不希望我们的软件包被用作库,我们可能已经使用了devDependencies
。
危险信号
/// <reference path="..." />
不要在声明文件中使用/// <reference path="..." />
。
ts
/// <reference path="../typescript/lib/typescriptServices.d.ts" />....
请改为使用/// <reference types="..." />
。
ts
/// <reference types="typescript" />....
请务必重新访问使用依赖项部分以获取更多信息。
打包依赖声明
如果你的类型定义依赖于另一个软件包
- 不要将其与您的文件合并,让每个文件都保持在自己的文件中。
- 不要复制包中的声明。
- 要依赖 npm 类型声明包,如果它不打包其声明文件。
使用 typesVersions
进行版本选择
当 TypeScript 打开 package.json
文件以找出它需要读取哪些文件时,它首先会查看一个名为 typesVersions
的字段。
文件夹重定向(使用 *
)
带有 typesVersions
字段的 package.json
可能如下所示
json
{"name": "package-name","version": "1.0.0","types": "./index.d.ts","typesVersions": {">=3.1": { "*": ["ts3.1/*"] }}}
此 package.json
告诉 TypeScript 首先检查 TypeScript 的当前版本。如果它是 3.1 或更高版本,TypeScript 就会找出您相对于包导入的路径,并从包的 ts3.1
文件夹中读取。
这就是 { "*": ["ts3.1/*"] }
的含义 - 如果您熟悉 路径映射,它的工作方式完全相同。
在上面的示例中,如果我们从 "package-name"
导入,TypeScript 将尝试在 TypeScript 3.1 中运行时从 [...]/node_modules/package-name/ts3.1/index.d.ts
(和其他相关路径)中解析。如果我们从 package-name/foo
导入,我们将尝试查找 [...]/node_modules/package-name/ts3.1/foo.d.ts
和 [...]/node_modules/package-name/ts3.1/foo/index.d.ts
。
如果在这个示例中我们没有在 TypeScript 3.1 中运行,会怎样?好吧,如果 typesVersions
中的任何字段都没有匹配,TypeScript 将回退到 types
字段,因此这里的 TypeScript 3.0 和更早版本将被重定向到 [...]/node_modules/package-name/index.d.ts
。
文件重定向
当您只想一次更改单个文件的分辨率时,您可以通过传入确切的文件名来告诉 TypeScript 以不同的方式解析文件
json
{"name": "package-name","version": "1.0.0","types": "./index.d.ts","typesVersions": {"<4.0": { "index.d.ts": ["index.v3.d.ts"] }}}
在 TypeScript 4.0 及更高版本中,"package-name"
的导入将解析为 ./index.d.ts
,而在 3.9 及更低版本中解析为 "./index.v3.d.ts
。
请注意,重定向仅影响包的外部 API;项目内的导入解析不受 typesVersions
影响。例如,在前面的示例中,包含 import * as foo from "./index"
的 d.ts
文件仍将映射到 index.d.ts
,而不是 index.v3.d.ts
,而导入 import * as foo from "package-name"
的另一个包将获取 index.v3.d.ts
。
匹配行为
TypeScript 决定编译器和语言版本是否匹配的方式是使用 Node 的 semver 范围。
多个字段
typesVersions
可以支持多个字段,其中每个字段名称由要匹配的范围指定。
{"name": "package-name","version": "1.0"," ": "./index.d.ts","typesVersions": {">=3.2": { "*": ["ts3.2/*"] },">=3.1": { "*": ["ts3.1/*"] }}}
由于范围有可能重叠,因此确定应用哪个重定向是特定于顺序的。这意味着在上面的示例中,即使 >=3.2
和 >=3.1
匹配器都支持 TypeScript 3.2 及更高版本,但颠倒顺序可能会产生不同的行为,因此上面的示例不等于以下示例。
{"name": "package-name","version": "1.0"," ": "./index.d.ts","typesVersions": {// NOTE: this doesn't work!">=3.1": { "*": ["ts3.1/*"] },">=3.2": { "*": ["ts3.2/*"] }}}
发布到 @types
@types 组织下的包会使用 DefinitelyTyped 中的 types-publisher 工具 自动发布。若要将你的声明发布为 @types 包,请向 DefinitelyTyped 提交拉取请求。你可以在 贡献指南页面 中找到更多详细信息。