esbuild is so fast because it’s written in Go and uses a low-level, parallelized approach to parsing and transforming code, bypassing the JavaScript-based, single-threaded nature of tsc.
Here’s how you can use esbuild to transpile TypeScript to JavaScript, completely skipping the tsc compiler. We’ll go from a simple TypeScript file to a runnable JavaScript file.
Let’s start with a basic TypeScript file, hello.ts:
function greet(name: string): string {
return `Hello, ${name}!`;
}
const message = greet("World");
console.log(message);
First, you need to install esbuild. It’s available as an npm package:
npm install --save-dev esbuild
# or
yarn add --dev esbuild
Now, you can use the esbuild command-line interface to transpile hello.ts into JavaScript. By default, esbuild will output to stdout. To write to a file, use the --outfile flag.
npx esbuild hello.ts --outfile=hello.js --bundle --platform=node --format=cjs
Let’s break down these flags:
hello.ts: This is your input TypeScript file. You can list multiple files here.--outfile=hello.js: This specifies the output file name. If omitted, the JavaScript will be printed to the console.--bundle: This tells esbuild to bundle all your dependencies. If yourhello.tsimported other local.tsor.jsfiles, they would be included in the outputhello.js. For this simple example, it’s not strictly necessary but good practice.--platform=node: This targets the Node.js environment. This is important because it influences howimportandrequirestatements are handled and which built-in Node modules are considered available. Other options includebrowserandneutral.--format=cjs: This specifies the module format for the output JavaScript.cjs(CommonJS) is standard for Node.js. Other options includeesm(ECMAScript Modules) andiife(Immediately Invoked Function Expression).
After running the command, you’ll have a hello.js file. If you used the flags above, it would look something like this:
// hello.js
function greet(name) {
return `Hello, ${name}!`;
}
const message = greet("World");
console.log(message);
Notice that the TypeScript type annotations (: string) have been removed, as expected.
You can run this JavaScript file directly with Node.js:
node hello.js
This will output:
Hello, World!
Configuration Options
While the CLI is great for simple tasks, you’ll often want to use esbuild programmatically, especially for more complex build processes or when integrating with other tools. You can create an esbuild.config.js file.
// esbuild.config.js
require('esbuild').build({
entryPoints: ['hello.ts'],
bundle: true,
outfile: 'dist/bundle.js',
platform: 'node',
format: 'cjs',
// You can add more options here
minify: true, // Minifies the output
sourcemap: true, // Generates a source map
target: ['es2020', 'node14'], // Specify target ECMAScript version and Node.js version
}).catch(() => process.exit(1));
Then, you can run this configuration file using Node.js:
node esbuild.config.js
This approach allows for much finer control. For instance, the target option is crucial. By default, esbuild targets a relatively modern JavaScript version. If you need to support older Node.js versions or browsers, you’d specify that.
npx esbuild src/index.ts --outfile=dist/index.js --bundle --platform=browser --format=esm --target=es2015
This command transpiles src/index.ts for a browser environment, bundling it into a single dist/index.js file using ECMAScript Modules, targeting the ES2015 standard.
Advanced Features & Considerations
esbuild can handle more than just basic TypeScript transpilation. It supports JSX, CSS, and various other transformations. You can also configure it to watch for file changes and rebuild automatically.
npx esbuild src/app.ts --outfile=dist/app.js --bundle --platform=node --format=cjs --watch --incremental
--watch: This flag makes esbuild watch the input files and rebuild whenever they change.--incremental: This flag enables incremental builds, where esbuild caches build results and only recompiles affected parts of the project, making subsequent builds much faster after the initial one.
When esbuild encounters import statements, it resolves them based on your tsconfig.json’s moduleResolution and paths settings if you use it programmatically with tsconfig.json, or it infers them based on standard Node.js/browser module resolution when using the CLI.
One of the most powerful aspects is its speed, which is often orders of magnitude faster than tsc or Webpack for bundling and transpilation. This is due to its Go implementation and its ability to leverage multiple CPU cores efficiently.
It’s important to understand that while esbuild transpiles TypeScript, it does not perform type checking. Type checking is a separate process. If you need full type safety, you’ll still need to run tsc --noEmit in your workflow. esbuild’s primary goal is fast compilation and bundling.
The next step in optimizing your build process might involve exploring esbuild’s plugin system to integrate custom transformations or leverage other tools for tasks esbuild doesn’t cover natively, like advanced code splitting or specific framework optimizations.