Menu

如何运行 TypeScript 文件:tsc、Node.js、tsx、ts-node

运行 .ts 文件的五种方式:用 tsc 编译后运行生成的 JavaScript、用 node file.ts 直接运行(类型剥离)、使用 tsx 或 ts-node,以及使用 Deno 和 Bun。本文说明哪些方式会检查类型、各自支持哪些语法,以及该如何选择。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

TypeScript 文件不能原样运行,因为浏览器和 JavaScript 引擎看不懂类型注解,必须先有工具把类型去掉。这个工具要么是 TypeScript 编译器(tsc),它同时会检查类型;要么是只剥离类型的更快的工具。运行本页代码最快的方式就是点击“运行”按钮:

输出:

[x] Install TypeScript
[ ] Run a .ts file

这些文档中的每个可运行代码块都是这样工作的:代码会先由开启了 strict 的 TypeScript 7 做类型检查,只有在没有类型错误时才会运行。想做更长的实验,可以用 TypeScript 在线编辑器,它是同一个编辑器,只是单独放在一个页面上。本页其余部分讲的是如何在你自己的电脑上运行 .ts 文件。

各种方式一览

命令检查类型需要构建步骤支持 enum、namespace、参数属性
先 npx tsc 再 node dist/index.js是是是
node index.ts(Node.js 22.18+、23.6+)否否否
npx tsx index.ts否否是
npx ts-node index.ts是否是,但不支持 TypeScript 7
deno run index.ts否(deno check 会检查)否是
bun index.ts否否是

最让人意外的是第一列:大多数快速的方式都会运行带有类型错误的代码。典型的项目会用其中一种方式运行代码,再在编辑器和 CI 中单独运行 tsc --noEmit 来发现错误。

先用 tsc 编译,再用 Node 运行

这种方式在任何地方都能用,而且会检查所有内容。在项目中安装了 TypeScript,并且 tsconfig.json 设置了 "rootDir": "./src" 和 "outDir": "./dist" 时:

npx tsc
node dist/index.js

tsc 会对所有文件做类型检查,然后把 .js 文件写入 dist。如果只是单个文件、没有项目,就传入文件名。这时它会使用默认选项,并把 index.js 写在 index.ts 旁边:

npx tsc index.ts
node index.js

(如果文件夹里有 tsconfig.json,tsc 会以 error TS5112 拒绝文件名参数;直接运行 npx tsc,或者加上 --ignoreConfig。)

默认情况下,即使存在类型错误,tsc 仍然会输出 JavaScript,所以 node 可能会运行一个没通过检查的程序。在配置中加上 "noEmitOnError": true 可以避免这种情况,或者在脚本中把命令串起来,让第二步只在第一步成功后才运行:

{
    "scripts": {
        "build": "tsc",
        "start": "tsc && node dist/index.js"
    }
}

开发时,npx tsc --watch 会在每次保存时重新编译。

用 Node.js 直接运行 TypeScript

当前版本的 Node.js 可以自己运行 .ts 文件:

node index.ts

Node 会剥离类型注解,用空白字符替换它们,让堆栈跟踪中的行号依然对得上,然后运行剩下的代码。这项功能从 Node.js 23.6.0 和 22.18.0 起默认开启,从 24.3.0 和 22.18.0 起不再打印警告,并在 Node.js 24.12.0 和 25.2.0 中被标记为稳定。更早的、已经带有该功能的版本(22.6 至 22.17,以及 23.0 至 23.5)需要加上参数:node --experimental-strip-types index.ts。

它有四条规则:

  • 不做类型检查。 包含 const age: number = "forty" 的文件照样能运行,并打印 forty。
  • 只支持可擦除的语法。 凡是需要变成 JavaScript 代码而不是直接消失的语法都会被拒绝:enum、带运行时代码的 namespace 块、像 constructor(private name: string) 这样的构造函数参数属性,以及 import x = require() 别名。Node 会以 SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode 终止运行。
  • 忽略 tsconfig.json。 paths 或 target 之类的选项都不起作用。
  • 导入时要写真实的文件名。 写成 import { add } from "./math.ts",带上扩展名,并用 type 标记只导入类型的部分:import { add, type Pair } from "./math.ts"。如果不加 type,Node 会去找名为 Pair 的运行时导出,并报错 SyntaxError: The requested module './math.ts' does not provide an export named 'Pair'。

有两个编译选项能让 tsc 执行同样的规则,这样编辑器会抢在 Node 之前提醒你:"erasableSyntaxOnly": true 会对 enum 报告 error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.,"verbatimModuleSyntax": true 要求只导入类型时加上 type 关键字。如果想在导入中继续写 .ts 扩展名,同时还能用 tsc 编译,就加上 "rewriteRelativeImportExtensions": true,它会在输出中把 ./math.ts 改成 ./math.js。

Node.js 24 还有 --experimental-transform-types,它会为枚举和参数属性生成代码,而不是拒绝它们。它会打印 ExperimentalWarning,而且 Node.js 26 已经移除了这个参数,所以不要依赖它。

下面这个代码块用到了 node index.ts 会拒绝的两个特性。它在这里能运行,是因为编辑器用 TypeScript 编译器来编译它,而编译器会为这两者生成 JavaScript:

同一段代码的可擦除版本用 const 对象和普通字段来写,Node 可以原样运行:

tsx

tsx 能一步运行 TypeScript 文件,无需任何配置,也不限制语法:

npm install --save-dev tsx
npx tsx index.ts
npx tsx watch index.ts   # rerun on every change

它用 esbuild 转换代码,所以枚举、命名空间和参数属性都能用,不带扩展名的导入也会像在打包工具中那样被解析。和 Node 的类型剥离一样,它不做类型检查。在还不支持类型剥离的 Node.js 版本上,或者代码用到了 Node 会拒绝的语法时,它是运行脚本、开发服务器和测试的常见选择。

ts-node

多年来,ts-node 一直是在 Node.js 上运行 TypeScript 的标准方式,很多教程和老项目至今仍在使用它(npx ts-node index.ts、node -r ts-node/register)。它默认会借助 TypeScript 编译器的 JavaScript API 做类型检查。

而这个 API 恰恰是 TypeScript 7 不再提供的:它的编译器是一个原生程序,typescript 7 包没有向 JavaScript 暴露任何编译器 API。安装了 TypeScript 7 后,ts-node 还没运行任何代码就会崩溃:

TypeError: Cannot read properties of undefined (reading 'fileExists')
    at readConfig (/project/node_modules/ts-node/dist/configuration.js:91:33)

ts-node 的最新版本 10.9.2 发布于 2023 年 12 月。新代码请使用 tsx 或 node index.ts。依赖 ts-node 的现有配置,只要项目继续使用 TypeScript 6(npm install --save-dev typescript@6)并且有一个 tsconfig.json(哪怕是空的 {}),就能继续工作。如果没有这个文件,ts-node 会退回到内置的默认设置,其中包含 TypeScript 6 已弃用的 node10 模块解析方式,于是 npx ts-node index.ts 既不运行文件,也不打印错误就直接退出了。

Deno 与 Bun

这两个运行时都把 TypeScript 当作一等公民的文件类型:

deno run index.ts    # runs without checking
deno check index.ts  # type-checks, reports errors, runs nothing

bun index.ts         # runs without checking

它们都不需要安装 typescript,也不需要 tsconfig.json,并且都支持 enum 和其他不可擦除的特性。Deno 自带一份 TypeScript 编译器,供 deno check 使用。Bun 只剥离类型,所以在 Bun 项目中你仍然需要安装 typescript,并运行 tsc --noEmit 来找出类型错误。

只有 tsc 会因类型错误而阻止程序运行

只有先运行 tsc 的方式才会拒绝运行带类型错误的程序。本页的编辑器就是其中之一,所以下面这个代码块会停在编译器这一步:

index.ts(6,21): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

同样的代码保存为文件后,用 node index.ts、npx tsx index.ts 或 bun index.ts 运行,会正常运行并打印 12,因为 3 * "4" 会把字符串转换成数字。所以即使用更快的工具运行代码,也要在流程中保留 tsc --noEmit:

{
    "scripts": {
        "dev": "tsx watch src/index.ts",
        "typecheck": "tsc --noEmit"
    }
}

应该选择哪一种?

  • 学习或快速测试: 用这些页面上的“运行”按钮,或者在线编辑器。
  • 在新版 Node.js 上运行脚本或小工具: 用 node index.ts,并在配置中开启 erasableSyntaxOnly,让编辑器标出 Node 会拒绝的任何语法。
  • 任何 Node.js 项目、任何语法: 用 tsx 运行,用 tsc --noEmit 检查。
  • 库或任何要发布的代码: 用 tsc,因为它还会生成用户需要的 .d.ts 文件。
  • 前端代码: 你的打包工具或框架(Vite、Next.js、Angular CLI)会替你运行 TypeScript;再加上 tsc --noEmit 做检查。

常见问题

如何运行 TypeScript 文件?

经典做法分两步:npx tsc 把 .ts 编译成 .js,再用 node dist/index.js 运行输出结果。在 Node.js 22.18 或 23.6 及以上版本中,只要文件只用到可擦除的类型语法,也可以直接运行 node index.ts。npx tsx index.ts 能一步运行任何 TypeScript 文件。

Node.js 能直接运行 TypeScript 吗?

可以。从 Node.js 23.6 和 22.18 开始,node file.ts 无需任何参数就能运行:Node 会去掉类型注解,再运行剩下的代码。它不做类型检查,会忽略 tsconfig.json,并且拒绝需要生成代码的语法,比如 enum、带运行时代码的 namespace 以及构造函数参数属性。

ts-node 支持 TypeScript 7 吗?

不支持。ts-node 调用的是编译器的 JavaScript API,而 typescript 7 包不提供这个 API,所以它一启动就会崩溃(Cannot read properties of undefined (reading 'fileExists'))。它的最后一个版本是 2023 年 12 月发布的 10.9.2。请改用 tsx、Node 自带的类型剥离,或者让 ts-node 继续搭配 TypeScript 6 使用。

tsx 和 ts-node 有什么区别?

tsx 只负责去掉类型(借助 esbuild)并运行结果,所以启动很快,但从不报告类型错误。ts-node 默认会用 TypeScript 编译器做类型检查,因此更慢,并且依赖编译器的 JavaScript API。现在大多数项目用 tsx 或 node file.ts 来运行,再配合 tsc --noEmit 做检查。

有在线的 TypeScript 运行环境吗?

有。这些文档页面上的代码块和 Coddy 的 TypeScript 在线编辑器都会用 TypeScript 7 编译并运行你的代码,显示编译错误或程序输出。typescriptlang.org 上的官方 TypeScript Playground 会显示生成的 JavaScript 和错误信息。

Coddy 编程语言插图

和 Coddy 一起学编程

开始