发布 TypeScript 包
本文专门介绍有关 TypeScript 发布的内容。 发布是指通过 npm(或其他包管理器)分发为软件包; 这不是关于编译应用程序/服务器以在生产环境中运行(例如 PWA 和/或端点服务器)。
一些需要注意的重要事项
-
发布软件包中的所有内容都适用于此处。
-
像
main
这样的字段在*发布的*内容上运行,因此当 TypeScript 源代码被转译为 JavaScript 时,JavaScript 是发布的内容,并且main
将指向带有 JavaScript 文件扩展名的 JavaScript 文件(例如main.ts
→"main": "main.js"
)。 -
像
scripts.test
这样的字段在源代码上运行,因此它们将使用源代码的文件扩展名(例如"test": "node --test './src/**/*.test.ts'
)。
-
-
Node 通过称为“类型剥离”的过程运行 TypeScript 代码,其中 node(通过 Amaro)删除特定于 TypeScript 的语法,留下纯 JavaScript(node 已经理解)。 从 node 版本 23.6.0 开始,默认启用此行为。
- Node不剥离
node_modules
中的类型,因为它可能导致官方 TypeScript 编译器 (tsc
) 和 VS Code 的部分出现严重的性能问题,因此 TypeScript 维护者希望阻止人们发布原始 TypeScript,至少目前是这样。
- Node不剥离
-
在 node 中使用特定于 TypeScript 的功能(例如
enum
)仍然需要一个标志(--experimental-transform-types
)。无论如何,通常有更好的替代方法。- 为了确保不存在特定于 TypeScript 的功能(以便您的代码可以在 node 中直接运行),请在 TypeScript 版本 5.8+ 中设置
erasableSyntaxOnly
配置选项。
- 为了确保不存在特定于 TypeScript 的功能(以便您的代码可以在 node 中直接运行),请在 TypeScript 版本 5.8+ 中设置
-
使用 dependabot 使您的依赖项保持最新,包括 github 操作中的依赖项。 这是一个非常容易设置和忘记的配置。
-
.nvmrc
来自nvm
,这是一个用于 node 的多版本管理器。 它允许您指定项目通常应使用的 node 版本。
存储库的目录概述可能如下所示
example-ts-pkg/
├ .github/
│ ├ workflows/
│ │ ├ ci.yml
│ │ └ publish.yml
│ └ dependabot.yml
├ src/
│ ├ foo.fixture.js
│ ├ main.ts
│ ├ main.test.ts
│ ├ some-util.ts
│ └ some-util.test.ts
├ LICENSE
├ package.json
├ README.md
└ tsconfig.json
其发布的软件包的目录概述可能如下所示
example-ts-pkg/
├ LICENSE
├ main.d.ts
├ main.d.ts.map
├ main.js
├ package.json
├ README.md
├ some-util.d.ts
├ some-util.d.ts.map
└ some-util.js
关于目录组织的说明:放置测试有一些常见的做法。 最小知识原则说要将它们并置(将它们放在实现的旁边)。 有时,这在同一目录中,或者在抽屉中(如 __test__
)(也与实现相邻,“文件并置但分离”)。 或者,有些人选择创建一个与 src/
同级的 test/
(“'src' 和 'test' 完全分离”),可以使用镜像结构或“杂物抽屉”。
如何处理您的类型
像对待测试一样对待类型
类型的目的是警告实现将不起作用
const foo = 'a';
const bar: number = 1 + foo;
// ^^^ Type 'string' is not assignable to type 'number'.
TypeScript 已警告说,以上代码的行为将不如预期,就像单元测试警告代码的行为不如预期一样。 它们是互补的,并验证不同的内容——您应该两者都具备。
您的编辑器(例如 VS Code)可能具有对 TypeScript 的内置支持,可以在您工作时显示错误。 如果没有,和/或您错过了这些错误,CI 将为您提供支持。
以下 GitHub Action 设置了一个 CI 任务,以自动检查(并要求)类型通过对 main
分支的 PR 的检查。
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Tests
on:
pull_request:
branches: ['*']
jobs:
check-types:
# Separate these from tests because
# they are platform and node-version independent
# and need be run only once.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: npm clean install
run: npm ci
# You may want to run a lint check here too
- run: node --run types:check
get-matrix:
# Automatically pick active LTS versions
runs-on: ubuntu-latest
outputs:
latest: ${{ steps.set-matrix.outputs.requireds }}
steps:
- uses: ljharb/actions/node/matrix@main
id: set-matrix
with:
versionsAsRoot: true
type: majors
preset: '>= 22' # glob is not backported below 22.x
test:
needs: [get-matrix]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
node-version: ${{ fromJson(needs.get-matrix.outputs.latest) }}
os:
- macos-latest
- ubuntu-latest
- windows-latest
steps:
- uses: actions/checkout@v4
- name: Use node ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: npm clean install
run: npm ci
- run: node --run test
请注意,测试文件可能应用了不同的 tsconfig.json
(因此它们在上述示例中被排除在外)。
生成类型声明
类型声明(.d.ts
及其朋友)以 sidecar 文件的形式提供类型信息,允许执行代码为纯 JavaScript,同时仍然具有类型。
由于这些是根据源代码生成的,因此可以将它们构建为发布过程的一部分,而无需将它们检入到您的存储库中。
以下面的示例为例,其中类型声明在发布到 npm 注册表之前生成。
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
# This is mostly boilerplate.
name: Publish to npm
on:
push:
tags:
- '**@*'
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
# - name: Publish to npm
# run: … npm publish …
您将需要发布一个编译后的软件包,以支持所有 Node.js LTS 版本,因为您不知道消费者将运行哪个版本; 本文中的 tsconfig
支持 node 18.x 及更高版本。
npm publish
将自动运行 prepack
在之前。 npm
还将在 npm pack --dry-run
之前自动运行 prepack
(因此您可以轻松查看您发布的软件包将是什么样子,而无需实际发布它)。注意,node --run
不不这样做。您不能将 node --run
用于此步骤,因此该警告不适用于此处,但它可能适用于其他步骤。
实际发布到 npm 的步骤将包含在另一篇文章中(除了本文的范围之外,还有一些优点和缺点)。
分解一下
生成类型声明是确定性的:您每次都会从相同的输入获得相同的输出。 因此,无需将这些提交到 git。
npm publish
获取命令运行时适用的所有内容; 因此,立即生成类型声明意味着这些类型声明可用并将被提取。
默认情况下,npm publish
获取(几乎)所有内容(请参阅 软件包中包含的文件)。为了保持您发布的软件包尽可能小(请参阅有关 node_modules
的“宇宙中最重的对象”模因),您需要从打包中排除某些文件(如测试和测试固定装置)。 将这些添加到 .npmignore
中指定的选择退出列表; 确保已列出 !*.d.ts
异常,否则生成的类型声明将不会发布! 或者,您可以使用 package.json "files" 来创建选择加入(如果意外遗漏文件,您的软件包可能会对下游用户造成损坏,因此这不是一个安全的选项)。