Skip to Content

Execute (ttsx)

ttsx runs a TypeScript file directly, like tsx or ts-node, but it type-checks first and applies your plugins.

Run a file

npx ttsx src/index.ts

ttsx:

  1. Resolves tsconfig.json from the current directory (or -P path/to/tsconfig.json).
  2. Type-checks the project, including every plugin you’ve configured.
  3. If the check passes, executes the script natively.

Type errors stop execution. So do @ttsc/lint errors (warnings don’t).

Projects that enable allowImportingTsExtensions are supported. During the runtime emit, ttsx rewrites relative .ts, .tsx, .mts, and .cts import specifiers to the matching JavaScript extensions so Node can execute the output. The emitted JavaScript lives in a per-run cache directory and is removed after the script exits.

Raw-TypeScript dependencies

The type-check covers your whole program, but at runtime your dependencies are loaded by Node directly. When part of the graph is ESM, Node trips over dependencies that ship raw TypeScript:

  • A workspace dependency (a node_modules symlink whose real files live outside node_modules) gets its types stripped by Node, but its own extensionless relative imports (import "./util") are rejected with ERR_MODULE_NOT_FOUND.
  • A published dependency that ships .ts inside node_modules cannot be loaded at all, because Node refuses to strip types under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING).

ttsx installs Node module hooks in the run, the same approach tsx uses, so both cases just work without touching the dependency’s exports, sources, or tsconfig:

  • a resolve hook probes the candidate extensions (and directory index files) for an extensionless relative import anywhere in the graph;
  • a load hook transpiles raw .ts files under node_modules, scoped so workspace neighbours keep Node’s native type-stripping path.

These hooks are runtime-only and never weaken the compile gate: the up-front tsgo build still deep-checks the whole program, imported workspace neighbours included, so a type error in a dependency’s source fails the run before anything executes. A genuinely missing module still throws ERR_MODULE_NOT_FOUND. This makes ttsx strictly stronger than tsx: a full type-check plus the same runtime reach.

When an ESM entry imports named bindings from a CommonJS-classified source package, ttsx also exposes names re-exported through nested export * barrels. Node’s CommonJS interop can miss those names because TypeScript-Go lowers them through dynamic helper calls, so the runtime hook makes the emitted CommonJS names visible without changing the package’s CommonJS require path.

Pass arguments to the script

Use -- to separate ttsx flags from the script’s own args:

npx ttsx src/server.ts -- --port 3000 --watch

Everything after -- is passed through to process.argv unchanged.

Preload modules

Same as Node’s --require:

npx ttsx -r ./preload.cjs src/index.ts npx ttsx -r dotenv/config src/index.ts

Repeatable: -r a -r b preloads a then b. These modules are passed to Node’s raw --require loader; ttsx does not compile preload files for you.

Pick a tsconfig

npx ttsx -P tsconfig.scripts.json src/seed.ts

Flags

FlagMeaning
-P, --project <file>Use an explicit tsconfig.json.
--cwd <dir>Resolve the entrypoint and project from this directory.
--cache-dir <dir>Override the runtime and source-plugin cache root.
--binary <path>Use an explicit TypeScript-Go binary.
--no-pluginsBuild the entry’s project without loading ttsc plugins.
-r, --require <module>Preload a module before the entrypoint. Repeatable.
--singleThreadedRun TypeScript-Go single-threaded. Mirrors tsc --singleThreaded.
--checkers <n>Type-checker pool size. Mirrors tsc --checkers.
-h, --helpShow help.
-v, --versionPrint the runner version.

Any flag ttsx does not own, placed before the entry file, is forwarded to the tsgo type-check, ttsx --strict src/index.ts works like the matching tsgo invocation. Flags after the entry go to the running program as its own process.argv, the same as node.

When ttsx vs ttsc

TaskUse
One-off script, fast feedbackttsx src/script.ts
Build for deploymentttsc
Run inside CI to gate on typesttsc --noEmit
Run inside CI to gate on types and execute teststtsx test/index.ts

ttsx vs tsx vs ts-node

tsxts-nodettsx
Transpile speedFastSlowFast
Type-checks before runningNoNo (by default)Yes
Plugin supportNoCustom transformersFirst-class
TypeScript-Go runtimeNoNoYes

If you currently use tsx: switching is one command; ergonomics are the same.

Plugins and cache

ttsx uses the same tsconfig.json and plugins as ttsc. If a lint or check plugin reports an error, the script never runs. Transform plugins rewrite the code before it executes.

Compiled JavaScript is temporary and cleaned after the script exits. Source plugin binaries still use the shared ttsc plugin cache, so repeated runs do not rebuild unchanged Go sidecars. Use --cache-dir when you want both the temporary runtime output root and the source-plugin binary cache anchored in a known directory. Relative --cache-dir values resolve from --cwd; ttsx places runtime output under <cache-dir>/project/<pid> and plugin binaries under <cache-dir>/plugins. The runtime project subtree is still removed after each run. Use npx ttsc clean --cache-dir <dir> to wipe an explicit plugin cache.

See also

  • TTSC · ttsc CLI: the type-check / build front of the same pipeline.
  • Lint & Prettier: the linter that runs alongside the compile.
  • FAQ: common runner gotchas and Node-version questions.
Last updated on