Using `tsc` and `ts-node` for production builds
Table of Contents
What I was comparing #
When a Node.js server is written in TypeScript, there are two common ways to run it.
- Compile the TypeScript with
tsc, then run the generated JavaScript with Node.js. - Use
ts-nodeto transform and execute TypeScript during startup.
The difference is not only a command. It changes when TypeScript is transformed and what the production process has to carry.
Using tsc #
tsc compiles the project according to tsconfig.json.
tsc -p ./
node dist/server.js
The build step creates JavaScript before the server starts. A production image or deployment can then contain the compiled output and the runtime dependencies needed to execute it.
This also gives the build pipeline a clear failure point. If the TypeScript does not compile, the deployment can stop before starting the server.
Using ts-node #
ts-node hooks into Node.js module loading and transforms TypeScript when a file is loaded.
ts-node --transpile-only src/server.ts
This is convenient during development because the source files can be executed without a separate compile command. The process also needs the TypeScript execution tool and the source configuration at runtime.
The --transpile-only option skips type checking. That can shorten the startup path, but type checking still needs to happen in a separate build or CI step.
The production decision #
For production, I prefer compiling with tsc and running the generated JavaScript.
{
"scripts": {
"build": "tsc -p ./",
"start": "node dist/server.js",
"dev": "ts-node src/server.ts"
}
}
This separates development convenience from the production runtime. It also makes the artifact that will be deployed explicit.
That does not make ts-node unusable in production. It can be a reasonable choice when the deployment already supports runtime TypeScript execution and the build and type-check steps are enforced elsewhere. The important part is knowing whether the server is failing during compilation, startup transformation, or application startup.
Conclusion #
tsc and ts-node solve related but different problems. tsc prepares JavaScript before the process starts, while ts-node transforms TypeScript as Node.js loads it.
For the production build flow I was considering, the precompiled output from tsc made the deployment boundary easier to understand. I would still use ts-node when the shorter development loop matters more than producing a separate artifact.