tsx vs ts-node: Running TypeScript in CI
tsx runs TypeScript fast via esbuild with painless ESM; ts-node is the established runner with optional type checking.
ts-node executes TypeScript directly in Node, optionally type-checking as it runs, but ESM setup can be fiddly. tsx is an esbuild-powered runner that strips types fast and handles ESM/CJS smoothly, without type checking on its own.
| ts-node | tsx | |
|---|---|---|
| Engine | tsc-based (type-aware option) | esbuild (transpile-only) |
| Type checking while running | Optional | No (run tsc separately) |
| ESM ergonomics | Can be fiddly | Smooth |
| Startup speed | Slower | Fast |
| Typical use | Run TS with type safety | Fast TS scripts / dev |
In CI
tsx is great for running TypeScript scripts and tools quickly in CI, especially with ESM, because it transpiles with esbuild and skips type checking for speed. Pair it with a separate tsc --noEmit step for type safety. ts-node can type-check as it runs, which some prefer for scripts, but it is slower and its ESM configuration is a frequent source of CI failures (the classic ERR_UNKNOWN_FILE_EXTENSION / loader issues).
Best practice
In most pipelines, run type checking once with tsc --noEmit and execute scripts with the fast transpile-only runner (tsx). This separates correctness (types) from execution (speed) cleanly and avoids double work.
The verdict
Want fast execution and painless ESM, with a separate tsc type-check step: tsx. Want type checking during execution and you have a working ts-node setup: ts-node. The common pattern is tsx to run plus tsc for types.