Node.js 最近在 22.6 版本(并在 23.6 版本中稳定)中添加了原生的 TypeScript 支持,这是一个令人欢迎的增强,简化了 TypeScript 用户的设置。这在社区内引发了关于 Node 的新功能与 Deno 现有的 TypeScript 集成相比如何的问题。

在这篇文章中,我们将探讨 Node 的 TypeScript 支持,并提供与 Deno 方法的清晰比较。

Node 中的 TypeScript

TypeScript 为 JavaScript 添加了类型,有助于在项目增长时构建你的代码。它主要处理两个任务:

  1. 类型检查: 确保你的变量与声明的类型匹配。
  2. 类型剥离: 将 TypeScript 转译为纯 JavaScript,可由浏览器和运行时执行。

Node 22.6.0 引入了内置的 TypeScript 支持,允许通过 --experimental-strip-types 标志自动进行类型剥离。此功能在 Node 23.6 中得到稳定,无需额外的标志即可直接执行 (node foo.ts)。

Node 的 TypeScript 支持将类型注解替换为空格,从而生成有效的 JavaScript:

function sum(a: number, b: number): number {
  return a + b;
}
sum(5, 10);
function sum(a        , b        )         {
  return a + b;
}
sum(5, 10);

这实际上是将以前由 ts-node 提供的功能直接集成到 Node 中,简化了 TypeScript 的执行。

然而,存在一些限制:

  • 没有内置的类型检查: 仍然需要像 tsc 这样的外部工具。
  • 不支持 JSX 或 TSX: Node 处理 .ts.mts.cts,但 React (.tsx) 和 JSX 项目仍然需要外部转译器或打包器,例如 esbuild、Babel 或 tsc
  • 手动管理 tsconfig.json 类型检查仍然依赖于通过 tsconfig.json 进行的外部配置。

以上是 Node 中 TypeScript 的概述——让我们看看 Deno 如何处理这些方面。

Deno 中的 TypeScript

Deno 通过提供一个具有完全集成的 TypeScript 工具链的单个可执行文件来简化 Web 编程。这种方法以最少的配置提供 TypeScript 的优势,从而简化了测试、格式化和编译工作流程。

Deno 的 TypeScript 集成包括三个主要部分:

  1. 执行: Google 的 V8 引擎,它执行 JavaScript,但不能直接执行 TypeScript。
  2. 类型检查: Microsoft 的 TypeScript 编译器(用 JavaScript 实现)在内部捆绑。
  3. 类型剥离: SWC,一个由 Kang Dong Yoon (강동윤) 用 Rust 构建的高性能解析器,高效地剥离类型,而无需运行 JavaScript。

tsconfig.json 呢?

Deno 强调零配置开发,以避免配置过载。它提供了适用于大多数场景的合理默认值(详情请点击此处)。如果需要自定义选项,你可以轻松地在 deno.json 中使用 compilerOptions,或者指定你自己的 tsconfig.json

deno -c tsconfig.json main.ts

在 CI 中使用 deno check 进行类型检查

运行 deno check 可以快速识别类型错误,无论是在本地还是远程:

deno check
deno check main.ts
deno check jsr:@std/http/file-server
deno check --all
deno check --doc
deno check --doc-only

使用 LSP 实现类型感知的工具提示、错误等

当使用 Deno 的语言服务器 (LSP),特别是与像 VSCode 这样的编辑器一起使用时,你将获得即时的类型感知反馈和 linting。

Image 1: 来自 JSR 依赖的 VSCode 中的类型检查

VSCode 中 JSR 依赖的实时类型检查。无需配置。

Image 2: 显示文档的悬停工具提示

显示文档的悬停工具提示。

deno repldeno jupyter 中的 TypeScript

你可以在 Deno 的 REPLJupyter Notebooks 中直接运行 TypeScript:

$ deno
Deno 2.2.2
exit using ctrl+d, ctrl+c, or close()
REPL is running with all permissions allowed.
> const sum = function(a: number, b: number): number { return a + b };
undefined
> sum(5, 10)
15

(注意:REPL 将类型视为注释;没有实时检查。)

无需转译即可分发 TypeScript

Deno 支持直接分发 TypeScript 模块,无需转译步骤和单独的 .d.ts 文件。

JSR 注册表 利用了这一点,促进了无缝的 TypeScript 分发,而不会损失清晰度或可读性:

import { encodeBase64 } from "jsr:@std/encoding@1";

借助 JSR,调试仍然清晰明了——堆栈跟踪和源码导航直接链接到原始 TypeScript 代码。

TypeScript 和 npm

Deno 通过 package.jsonnpm: 说明符导入 npm 模块,并通过 .d.ts 文件提供完整的类型检查支持。对于没有类型的包,请使用 @ts-types pragma:

import express from "npm:express";

Deno 工具链中的 TypeScript 支持

Deno 在其整个工具链中广泛集成了 TypeScript:

此外,.tsx.jsx 支持使 React 或 Preact 无缝衔接。