現在您已經按照本指南的步驟編寫了宣告檔案,是時候將其釋出到 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。
警示標誌(Red flags)
/// <reference path="..." />
不要在您的宣告檔案中使用 /// <reference path="..." />。
ts/// <reference path="../typescript/lib/typescriptServices.d.ts" />....
要改用 /// <reference types="..." />。
ts/// <reference types="typescript" />....
請務必重溫 消費依賴項 (Consuming dependencies) 一節以獲取更多資訊。
打包依賴宣告
如果您的型別定義依賴於另一個包:
- 不要將它與您的定義合併,請將每個定義儲存在各自的檔案中。
- 也不要在您的包中複製這些宣告。
- 要依賴該 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/*"] } 的含義——如果您熟悉 路徑對映 (path mapping),它的工作原理完全相同。
在上面的示例中,如果我們從 "package-name" 匯入,當在 TypeScript 3.1 中執行時,TypeScript 將嘗試從 [...]/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 組織下的包是使用 types-publisher 工具從 DefinitelyTyped 自動釋出的。要將您的聲明發布為 @types 包,請向 DefinitelyTyped 提交拉取請求。您可以在 貢獻指南頁面中找到更多詳細資訊。