TL;DR
Converting TypeScript to JavaScript means stripping type annotations, interfaces, enums, and other TS-only syntax to produce valid JS. You can use the official tsc compiler, our free online converter, Babel with @babel/preset-typescript, esbuild (the fastest option), or SWC (Rust-based speed). This guide covers every method, common pitfalls with enums and decorators, how to preserve JSDoc for IDE support, and integration with Vite, webpack, and Rollup build pipelines.
Key Takeaways
- The TypeScript compiler (
tsc) is the most accurate way to convert TypeScript to JavaScript but is also the slowest for large codebases. - esbuild and SWC perform type-stripping only (no type checking) and are 10-100x faster than
tsc. - Babel with
@babel/preset-typescriptstrips types while keeping your existing Babel plugin pipeline intact. - TypeScript enums, namespaces, and
const enumrequire special handling because they emit runtime JavaScript code. - Decorators (experimental or TC39 Stage 3) need transpilation, not just type stripping.
- Converting JSDoc comments preserves IDE IntelliSense in the output JavaScript files.
- Our TypeScript to JavaScript converter handles all these edge cases in a single click.
Why Convert TypeScript to JavaScript?
TypeScript has become the default language for modern web development, with adoption growing rapidly year over year. But there are many practical scenarios where you need to convert TypeScript back to plain JavaScript. Understanding when and why this conversion is necessary helps you choose the right tool and approach.
Publishing npm packages: Most npm packages ship compiled JavaScript alongside type declaration files (.d.ts). This ensures compatibility with consumers who do not use TypeScript. The tsc compiler generates both the JavaScript output and the declaration files in a single pass.
Running in browsers: No browser natively executes TypeScript. Whether you use a bundler like Vite or webpack, the TypeScript code must be transformed to JavaScript before it reaches the browser. Understanding this compilation step helps you debug source map issues and optimize build times.
Migrating away from TypeScript: Some teams decide to remove TypeScript from their stack. This might happen when a project is being handed to a team less familiar with TypeScript, or when the type maintenance overhead exceeds the benefits for a small project. In such cases, a bulk TS-to-JS conversion is needed.
Learning and prototyping: Developers learning JavaScript sometimes encounter TypeScript examples online and need to understand the equivalent JavaScript. An online TS to JS converter makes this translation instant.
Legacy system integration: Older build systems, serverless platforms, or embedded environments may only support vanilla JavaScript. Converting TypeScript output to a specific ES version (ES5, ES2015, ES2020) ensures compatibility with these constrained runtimes.
Method 1: Using the TypeScript Compiler (tsc)
The official TypeScript compiler is the most reliable way to convert TS to JS. It performs full type checking, handles all TypeScript syntax including enums and namespaces, and produces accurate JavaScript output. Here is how to set it up:
Installation
# Install TypeScript globally
npm install -g typescript
# Or install locally in your project
npm install --save-dev typescript
# Verify installation
tsc --versionBasic Compilation
# Compile a single file
tsc app.ts
# Produces app.js in the same directory
# Compile with a specific target
tsc --target ES2020 app.ts
# Compile an entire project using tsconfig.json
tsc
# Watch mode for development
tsc --watchtsconfig.json Configuration
A well-configured tsconfig.json controls exactly how TypeScript is converted to JavaScript. Here are the key compiler options relevant to JS output:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationDir": "./dist/types",
"sourceMap": true,
"removeComments": false,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}| Option | Effect on JS Output |
|---|---|
target | Controls which ES version the output uses (ES5, ES2015, ES2020, ESNext) |
module | Sets the module system: CommonJS (require), ESNext (import) |
declaration | Generates .d.ts files alongside JS output |
sourceMap | Generates .js.map files for debugging |
removeComments | Strips all comments from output JS |
outDir | Directory where compiled JS files are written |
Example: TypeScript Input vs JavaScript Output
TypeScript Input:
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
type Role = 'admin' | 'editor' | 'viewer';
function greetUser(user: User, role: Role): string {
const greeting: string = `Hello, ${user.name}!`;
return `${greeting} You are logged in as ${role}.`;
}
const users: User[] = [
{ id: 1, name: 'Alice', email: 'alice@example.com', isActive: true },
{ id: 2, name: 'Bob', email: 'bob@example.com', isActive: false },
];
const activeUsers: User[] = users.filter(
(user: User): boolean => user.isActive
);
export { greetUser, activeUsers };JavaScript Output (ES2020 target):
function greetUser(user, role) {
const greeting = `Hello, ${user.name}!`;
return `${greeting} You are logged in as ${role}.`;
}
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com', isActive: true },
{ id: 2, name: 'Bob', email: 'bob@example.com', isActive: false },
];
const activeUsers = users.filter(
(user) => user.isActive
);
export { greetUser, activeUsers };Notice how the interface, type alias, and all type annotations are completely removed. The runtime logic remains identical. This is the fundamental operation of any TypeScript to JavaScript converter.
Method 2: Online Converter Tools
When you need a quick conversion without installing anything, an online converter is the fastest path. Our TypeScript to JavaScript online converter handles the full TypeScript syntax including generics, enums, interfaces, type guards, and decorators.
When to use an online converter:
- Quick one-off conversions while learning or debugging
- Converting code snippets from TS documentation or Stack Overflow answers
- Sharing JavaScript versions of TypeScript examples with team members who do not use TS
- Verifying what JavaScript output the TypeScript compiler would produce
- Converting single files without setting up a build pipeline
You can also use our TS to JS converter which provides the same functionality with additional formatting options. Both tools run entirely in your browser, so your code never leaves your machine.
Method 3: Babel for TypeScript Stripping
Babel can strip TypeScript types using the @babel/preset-typescript preset. This approach is ideal when you already have a Babel-based build pipeline and want to add TypeScript support without replacing your existing setup.
Installation
npm install --save-dev @babel/core @babel/cli \
@babel/preset-env @babel/preset-typescriptbabel.config.json
{
"presets": [
["@babel/preset-env", { "targets": "> 0.25%, not dead" }],
"@babel/preset-typescript"
]
}Usage
# Compile a single file
npx babel src/app.ts --out-file dist/app.js
# Compile entire directory
npx babel src --out-dir dist --extensions ".ts,.tsx"
# Watch mode
npx babel src --out-dir dist --extensions ".ts,.tsx" --watchImportant limitation: Babel does not perform type checking. It only strips types. This means type errors in your TypeScript code will not be caught during the Babel compilation step. You should run tsc --noEmit separately (for example, in your CI pipeline or as a pre-commit hook) to catch type errors.
Babel also cannot handle certain TypeScript-specific features that require type information:
const enumdeclarations (Babel replaces them with regular enums)namespacemerging across files- Legacy
export =syntax
Method 4: esbuild (Fastest Approach)
esbuild is a JavaScript bundler written in Go that compiles TypeScript to JavaScript at extraordinary speed. It is typically 10-100x faster than tsc and is the engine powering Vite during development. If build speed is your primary concern, esbuild is the best choice.
Installation and Basic Usage
npm install --save-dev esbuild
# Transform a single file (type-strip only)
npx esbuild src/app.ts --outfile=dist/app.js
# Bundle with tree-shaking
npx esbuild src/index.ts --bundle --outfile=dist/bundle.js \
--format=esm --target=es2020
# Multiple entry points
npx esbuild src/index.ts src/worker.ts \
--outdir=dist --format=esmProgrammatic API
import { build } from 'esbuild';
await build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
format: 'esm',
target: 'es2020',
sourcemap: true,
minify: process.env.NODE_ENV === 'production',
treeShaking: true,
});Like Babel, esbuild performs type stripping only. It does not validate types. Run tsc --noEmit separately for type checking. esbuild also does not support const enum or legacy namespace merging.
Performance Comparison
| Tool | 1,000 Files | Type Checking | Language |
|---|---|---|---|
tsc | ~15-30s | Yes (full) | TypeScript (JS) |
babel | ~8-15s | No | JavaScript |
esbuild | ~0.3-1s | No | Go |
swc | ~0.5-1.5s | No | Rust |
Method 5: SWC -- Rust-Based Speed
SWC (Speedy Web Compiler) is a Rust-based JavaScript/TypeScript compiler that offers performance comparable to esbuild. It is the default compiler in Next.js and is used by tools like Parcel and Deno. SWC supports a broader range of TypeScript features than esbuild, including decorators.
Installation and Usage
npm install --save-dev @swc/core @swc/cli
# Compile a single file
npx swc src/app.ts -o dist/app.js
# Compile a directory
npx swc src --out-dir dist
# Watch mode
npx swc src --out-dir dist --watch.swcrc Configuration
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true,
"dynamicImport": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2020"
},
"module": {
"type": "es6"
},
"sourceMaps": true
}SWC advantages over esbuild:
- Full decorator support (both legacy and TC39 Stage 3)
- Better
const enumhandling via plugin system - Used by Next.js, so it is battle-tested at scale
- Plugin system for custom transformations
Handling Common Conversion Challenges
Not all TypeScript features are simple type annotations that can be erased. Some TypeScript constructs emit runtime JavaScript code and require careful handling during conversion.
Enums
TypeScript enums compile to JavaScript objects with forward and reverse mappings. This is one of the few TypeScript features that produces new runtime code rather than just type annotations:
TypeScript:
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}
enum StatusCode {
OK = 200,
NotFound = 404,
ServerError = 500,
}
function move(direction: Direction): void {
console.log(`Moving ${direction}`);
}Compiled JavaScript:
var Direction;
(function (Direction) {
Direction["Up"] = "UP";
Direction["Down"] = "DOWN";
Direction["Left"] = "LEFT";
Direction["Right"] = "RIGHT";
})(Direction || (Direction = {}));
var StatusCode;
(function (StatusCode) {
StatusCode[StatusCode["OK"] = 200] = "OK";
StatusCode[StatusCode["NotFound"] = 404] = "NotFound";
StatusCode[StatusCode["ServerError"] = 500] = "ServerError";
})(StatusCode || (StatusCode = {}));
function move(direction) {
console.log(`Moving ${direction}`);
}const enum caveat: The const enum keyword tells TypeScript to inline enum values at compile time rather than creating a runtime object. Only tsc handles this correctly. Babel and esbuild convert const enum to regular enum, which changes the runtime behavior and bundle size.
Decorators
TypeScript decorators (commonly used with Angular, NestJS, and TypeORM) generate complex JavaScript wrapper functions:
// TypeScript with decorators
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${key} with`, args);
return original.apply(this, args);
};
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
}To compile decorators you need either tsc with experimentalDecorators: true or SWC with legacyDecorator: true. esbuild has limited decorator support. If your codebase uses decorators extensively, SWC or tsc are the recommended converters.
Namespaces
TypeScript namespaces (formerly called internal modules) compile to immediately-invoked function expressions (IIFEs):
// TypeScript
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}
export class EmailValidator implements StringValidator {
isValid(s: string): boolean {
return /^[^@]+@[^@]+$/.test(s);
}
}
}
// Compiled JavaScript
var Validation;
(function (Validation) {
class EmailValidator {
isValid(s) {
return /^[^@]+@[^@]+$/.test(s);
}
}
Validation.EmailValidator = EmailValidator;
})(Validation || (Validation = {}));TypeScript Features That Do Not Exist in JavaScript
Understanding which TypeScript features have JavaScript equivalents and which are purely compile-time helps you predict the output of any TypeScript to JavaScript converter:
| Feature | Compile-Time Only? | JS Output |
|---|---|---|
interface | Yes | Completely removed |
type alias | Yes | Completely removed |
Type annotations (: string) | Yes | Stripped |
Generics (<T>) | Yes | Stripped |
as type assertion | Yes | Stripped |
enum | No | IIFE creating runtime object |
const enum | Partially (inlined by tsc) | Inlined values or IIFE (depends on tool) |
namespace | No | IIFE wrapper |
| Decorators | No | Helper function calls |
| Parameter properties | No | Constructor assignments |
import type | Yes | Completely removed |
Parameter properties are a common surprise. TypeScript allows shorthand constructor parameter declarations that generate property assignments:
// TypeScript
class Service {
constructor(
private readonly name: string,
public port: number,
protected host: string = 'localhost'
) {}
}
// Compiled JavaScript
class Service {
constructor(name, port, host = 'localhost') {
this.name = name;
this.port = port;
this.host = host;
}
}Preserving JSDoc Comments for IDE Support
When you remove types from TypeScript and convert to JavaScript, you lose IDE IntelliSense, autocomplete, and inline documentation. JSDoc comments can restore much of this functionality in plain JavaScript files:
// TypeScript original
interface Config {
host: string;
port: number;
debug?: boolean;
}
function createServer(config: Config): void {
// ...
}
// JavaScript with JSDoc (preserves IDE support)
/**
* @typedef {Object} Config
* @property {string} host - Server hostname
* @property {number} port - Port number
* @property {boolean} [debug] - Enable debug mode
*/
/**
* Creates a new server instance.
* @param {Config} config - Server configuration
* @returns {void}
*/
function createServer(config) {
// ...
}You can even enable TypeScript checking on JSDoc-annotated JavaScript files by adding // @ts-check at the top of the file or setting "checkJs": true in your tsconfig.json. This gives you a gradual migration path in both directions.
Automating JSDoc Generation
Tools like ts-to-jsdoc and typescript-to-jsdoc can automatically convert TypeScript type annotations to JSDoc comments during the conversion process. This is valuable when migrating a project away from TypeScript while preserving developer experience:
# Using ts-to-jsdoc
npx ts-to-jsdoc src/ --out-dir dist/
# The output JavaScript will include JSDoc equivalents
# of all TypeScript type annotationsBuild Pipeline Integration
Modern web applications use bundlers that handle TypeScript compilation as part of the build pipeline. Here is how the most popular tools integrate TypeScript-to-JavaScript conversion:
Vite
Vite uses esbuild for TypeScript compilation during development and Rollup for production builds. TypeScript works out of the box with zero configuration:
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
// TypeScript works out of the box
// esbuild handles TS compilation in dev
esbuild: {
target: 'es2020',
// Customize esbuild TypeScript options
tsconfigRaw: {
compilerOptions: {
experimentalDecorators: true,
},
},
},
build: {
target: 'es2020',
// Rollup handles production builds
rollupOptions: {
// ...
},
},
});For type checking during Vite builds, install vite-plugin-checker:
npm install --save-dev vite-plugin-checker
// vite.config.ts
import checker from 'vite-plugin-checker';
export default defineConfig({
plugins: [
checker({ typescript: true }),
],
});webpack
webpack requires a loader to process TypeScript files. The two main options are ts-loader (uses tsc) and babel-loader with @babel/preset-typescript:
// webpack.config.js â Option 1: ts-loader
module.exports = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
};
// webpack.config.js â Option 2: esbuild-loader (fastest)
const { EsbuildPlugin } = require('esbuild-loader');
module.exports = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'esbuild-loader',
options: {
target: 'es2020',
},
},
],
},
plugins: [new EsbuildPlugin({ target: 'es2020' })],
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
};Rollup
Rollup is commonly used for building libraries. The @rollup/plugin-typescript or rollup-plugin-esbuild handles TypeScript compilation:
// rollup.config.js â using esbuild (recommended)
import esbuild from 'rollup-plugin-esbuild';
export default {
input: 'src/index.ts',
output: [
{ file: 'dist/index.cjs', format: 'cjs' },
{ file: 'dist/index.mjs', format: 'esm' },
],
plugins: [
esbuild({
target: 'es2020',
sourceMap: true,
minify: false,
}),
],
};Choosing the Right Conversion Method
The best method depends on your specific requirements. Here is a decision guide:
| Scenario | Recommended Tool | Reason |
|---|---|---|
| Publishing an npm library | tsc | Generates .d.ts declaration files alongside JS |
| Quick one-off conversion | Online converter | No installation needed, instant results |
| Existing Babel pipeline | @babel/preset-typescript | Integrates with existing Babel plugins |
| Maximum build speed | esbuild | 10-100x faster than tsc, powers Vite |
| Decorator-heavy code (Angular/NestJS) | swc or tsc | Full decorator transform support |
| Next.js project | swc (built-in) | Default compiler, zero config needed |
| Full project migration from TS to JS | tsc + ts-to-jsdoc | Preserves IDE support via JSDoc |
Step-by-Step: Bulk Convert a TypeScript Project to JavaScript
If you need to convert an entire TypeScript project to JavaScript, follow this systematic approach:
- Audit your TypeScript features: Check if you use
enum,namespace, decorators, orconst enum. These affect which tool you can use. - Compile with tsc first: Run
tsc --outDir distto generate baseline JavaScript output. This confirms your code compiles cleanly. - Generate JSDoc annotations: If preserving IDE support matters, run
ts-to-jsdocto convert type annotations to JSDoc comments. - Rename files: Change all
.tsto.jsand.tsxto.jsx(or.jsif your bundler supports it). - Remove tsconfig.json: Replace with a
jsconfig.jsonif you want path aliases and module resolution hints for your IDE. - Update package.json: Remove
typescriptand@types/*dev dependencies. Update build scripts to remove thetscstep. - Test thoroughly: Run your entire test suite to verify that the converted JavaScript behaves identically to the original TypeScript.
# Quick bulk conversion script
#!/bin/bash
# 1. Compile TypeScript to JavaScript
tsc --outDir dist --declaration false --sourceMap false
# 2. Copy non-TS files
rsync -av --exclude='*.ts' --exclude='*.tsx' src/ dist/
# 3. Verify output
node dist/index.js
echo "Conversion complete. Check dist/ directory."Common Errors and Troubleshooting
Here are the most frequent issues developers encounter when converting TypeScript to JavaScript, along with their solutions:
Error: Cannot find module
// Problem: TypeScript path aliases don't resolve in JS
import { utils } from '@/lib/utils';
// Solution: Use relative paths or configure module resolution
import { utils } from '../lib/utils.js';
// Or configure jsconfig.json paths
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}Error: Unexpected token (enum/namespace)
// Problem: Babel/esbuild encounters const enum
const enum Color { Red, Green, Blue }
// Solution 1: Replace const enum with regular enum
enum Color { Red, Green, Blue }
// Solution 2: Replace with a plain object
const Color = {
Red: 0,
Green: 1,
Blue: 2,
} as const;
// In JS (after conversion):
const Color = { Red: 0, Green: 1, Blue: 2 };Error: File extension issues with ESM
// Problem: Node.js ESM requires .js extensions
import { helper } from './helper';
// Error: Cannot find module './helper'
// Solution: Add explicit .js extensions
import { helper } from './helper.js';
// This works even when the source file is helper.ts
// In tsconfig.json, set:
{
"compilerOptions": {
"moduleResolution": "nodenext",
"module": "nodenext"
}
}Frequently Asked Questions
Can I convert TypeScript to JavaScript without installing anything?
Yes. Use our online TypeScript to JavaScript converter which runs entirely in your browser. Paste your TypeScript code and get clean JavaScript output instantly. No npm packages, no build tools, no command line required.
Does converting TypeScript to JavaScript change the runtime behavior?
For pure type annotations (interfaces, type aliases, generics), removing them has zero effect on runtime behavior. However, enums, namespaces, parameter properties, and decorators produce runtime JavaScript code. The conversion of these features can produce slightly different output depending on which tool you use, though the intended behavior should remain the same.
What is the fastest way to compile TypeScript?
esbuild is the fastest TypeScript compiler, processing thousands of files in under a second. It is written in Go and performs only type stripping without type checking. For projects that need both speed and type safety, run esbuild for compilation and tsc --noEmit for type checking in parallel.
Should I use tsc or esbuild for my project?
Use tsc when you need declaration file generation (.d.ts), full const enum support, or namespace merging. Use esbuild when build speed is critical and you handle type checking separately. Many projects use both: esbuild for fast development builds and tsc for CI/CD type checking and declaration generation.
How do I preserve IntelliSense after removing TypeScript?
Add JSDoc comments to your JavaScript files. Modern IDEs like VS Code read JSDoc annotations and provide autocomplete, type hints, and inline documentation. You can even enable type checking on JS files with // @ts-check or "checkJs": true in jsconfig.json.
Can Babel handle all TypeScript features?
Babel handles most TypeScript features but has limitations with const enum (treats as regular enum), namespace merging across files, and the legacy export = syntax. For most projects, these limitations are not relevant. If your codebase uses these features extensively, use tsc or SWC instead.
What is the difference between ts-to-js-converter tools and the TypeScript compiler?
Online TS to JS converter tools and the tsc compiler perform the same fundamental operation: removing TypeScript-specific syntax to produce valid JavaScript. Online tools are convenient for quick conversions, while tsc is designed for project-wide compilation with configuration, source maps, and declaration file output.
For a deeper comparison of when to use TypeScript versus JavaScript, see our guide on TypeScript vs JavaScript: When to Convert.