Compile TypeScript to JavaScript
Learn how tsc converts .ts files to .js, what gets stripped during compilation, and the essential compiler flags every TypeScript developer uses.
Compiling TypeScript means running the tsc command to convert .ts files into .js files. The compiler does two things at once: it checks all your type annotations for errors, and it strips the types to produce plain JavaScript.
The output is standard JavaScript that runs in any browser or Node.js version. TypeScript never adds runtime behavior. It only removes things.
The Basic Compile Command
Running the compiler starts with a single file. Given a small TypeScript file called app.ts that declares one typed variable:
const greeting: string = "Hello, TypeScript!";
console.log(greeting);Compile it with a single command, which reads the file, checks its types, and writes a new JavaScript file next to it:
tsc app.tsRunning that command produces a brand new file called app.js sitting right next to the original app.ts in the same folder:
const greeting = "Hello, TypeScript!";
console.log(greeting);Only one thing happened here: the string annotation was removed, because types do not exist at runtime. Nothing else changed, since the default target in modern TypeScript already emits a recent JavaScript version that supports const natively. Older syntax only appears when you set an older target yourself, which is explained below.
What Gets Stripped and What Gets Transformed
Compilation involves two separate operations:
| Operation | What happens | Example |
|---|---|---|
| Type erasure | All type annotations, interfaces, type aliases, and generics are removed | type annotations, interfaces, and generic parameters all disappear |
| Syntax downleveling | Modern JavaScript syntax is converted to older syntax based on target | const to var, arrow functions to regular functions, template literals to string concatenation |
Type erasure always happens. Syntax downleveling depends on your target setting. Here is a more complete example showing both effects together, using an interface, an arrow function, and a template literal in one small file called modern.ts:
interface User {
name: string;
age: number;
}
const getUser = (id: number): User => {
return { name: `User ${id}`, age: 25 };
};
console.log(getUser(1).name);Compiling this with no target set lets TypeScript float to the latest ECMAScript version it supports, so only the types disappear:
const getUser = (id) => {
return { name: `User ${id}`, age: 25 };
};
console.log(getUser(1).name);The interface and its type annotations are gone, but the arrow function and template literal stay exactly as written, because the default target already supports them natively. Setting target to an older value like ES5 changes that, and downlevels the syntax on top of stripping the types:
var getUser = function (id) {
return { name: "User ".concat(id), age: 25 };
};
console.log(getUser(1).name);Setting target to ES5 tells the compiler to downlevel syntax for older environments: the arrow function becomes a regular function, and the template literal becomes .concat(). Types are stripped either way. Only the syntax changes, and only when you ask for it.
Essential Compiler Flags
These flags control how tsc behaves. Use them on the command line or in your tsconfig.json.
| Flag | What it does | When to use |
|---|---|---|
--target ES2022 | Emit modern JavaScript | Projects running on modern Node.js or recent browsers |
--module NodeNext | Use Node.js module system | Node.js projects |
--outDir ./dist | Put compiled files in a separate folder | Keeps source and output cleanly separated |
--noEmitOnError | Do not produce .js files if there are type errors | Prevents running broken code |
--watch | Recompile on every file save | Development workflow |
--strict | Enable all strict type checks | Every new project |
Combine flags directly on the command line when you want to override the config for a single run:
tsc app.ts --target ES2022 --noEmitOnErrorFor anything beyond a one-off command, put the same options in a tsconfig.json file at the project root instead, so every teammate and every CI run uses the same settings:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"outDir": "./dist",
"strict": true,
"noEmitOnError": true
},
"include": ["src"]
}With this config, running tsc with no arguments compiles every .ts file in src/ and outputs the JavaScript to dist/.
Compiling with Errors
By default, tsc produces JavaScript output even when there are type errors. This is deliberate: it lets you run partial migrations where some files still have issues.
// error.ts
function add(a: number, b: number) {
return a + b;
}
add("hello", 5); // type errorCompile this file the normal way. The compiler reports the type mismatch, but by default it does not stop the JavaScript from being written:
tsc error.tsCompiling prints the type error straight to the terminal, with the file, line, and column pointing exactly at the mismatched argument:
error.ts:5:5 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.Despite the error, error.js is still created and will run, producing "hello5" at runtime with no warning at all. To prevent JavaScript from being written when there is an error, add the --noEmitOnError flag:
tsc error.ts --noEmitOnErrorNow no error.js file appears. The compiler refuses to output JavaScript until you fix the type error. This is the safer default for production builds.
Watch Mode for Development
The --watch flag keeps tsc running and recompiles whenever you save a .ts file:
tsc --watchThe compiler starts, checks the project once, and then stays running in the terminal instead of exiting, printing a status line when it finds no problems:
[10:30:15 AM] Starting compilation in watch mode...
[10:30:15 AM] Found 0 errors. Watching for file changes.Save a file with a type error while tsc --watch is still running, and the terminal updates immediately without you running any command:
[10:30:45 AM] File change detected. Starting incremental compilation...
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
[10:30:45 AM] Found 1 error. Watching for file changes.Fix the error and save the file again, and the terminal clears itself and confirms the project compiles cleanly right away:
[10:31:02 AM] File change detected. Starting incremental compilation...
[10:31:02 AM] Found 0 errors. Watching for file changes.Watch mode gives you a fast feedback loop. Keep a terminal open with tsc --watch running, and you see type errors the moment you save.
Compiling an Entire Project
For projects with multiple files, you need a tsconfig.json. Create one at the project root:
tsc --initThis generates a tsconfig.json with every option listed and commented. Edit it to set target, module, outDir, and strict. Then run tsc with no arguments:
tscThe compiler reads tsconfig.json, finds every .ts file in the project, and compiles them all. For more on project setup, see TypeScript project setup for beginners.
The Compiled Output is Just JavaScript
The most important thing to remember: after compilation, you have plain .js files. You can run them with Node.js, serve them to a browser, or bundle them with any JavaScript tool. TypeScript does not lock you into a special runtime. It produces standard JavaScript that works everywhere.
For help understanding what the compiler tells you when something is wrong, see read TypeScript compiler errors.
Rune AI
Key Insights
- Compile a single file with
tsc filename.ts. Compile a project with justtsc(requires tsconfig.json). - The compiler does two things: type checking and JavaScript output. Types are always stripped.
- Use
--noEmitOnErrorto prevent JavaScript output when there are type errors. - Use
--watchto recompile automatically on every file save. - The
targetoption controls which JavaScript version is emitted.modulecontrols the module system.
Frequently Asked Questions
Does tsc only strip types, or does it also transform JavaScript syntax?
Can I compile TypeScript without a tsconfig.json file?
Why does my compiled JavaScript look different from my TypeScript?
Conclusion
Compiling TypeScript is a single command: tsc. The compiler does two things: checks your types for errors, then strips them and outputs plain JavaScript. The output is determined by your target and module settings. Use --watch during development, --noEmitOnError to prevent broken JavaScript, and --outDir to keep your source and output folders separate. The compiled JavaScript is standard JavaScript that runs anywhere Node.js or a browser runs.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.