TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
215: esbuild and swc for Fast Compilation
I remember the first time I integrated esbuild into a massive enterprise project. We were spending nearly two minutes waiting for tsc to finish every time we wanted to test a change. I swapped it out for esbuild, and suddenly, the build took 200 milliseconds. I felt like a wizard. Then, an hour later, the app crashed in production with a TypeError: cannot read property 'id' of undefined.
Here is the code that caused the disaster. I had renamed a property in my User interface, but I missed one reference in a utility function:
// types.ts
export interface User {
userId: string; // Changed from 'id' to 'userId'
username: string;
}
// userService.ts
import { User } from './types';
export function formatUser(user: User) {
// Oops! I forgot to update this to user.userId
return `User: ${user.id}`;
}
The "Instant Build" Illusion
If you were using tsc, the compiler would have screamed at you immediately. You wouldn't even have been able to run the code. But because I was using esbuild, the build finished instantly and the code ran. Why? Because esbuild doesn't actually "compile" TypeScript in the way you think it does. It transpiles it.
esbuild (and its primary competitor, SWC) operates on a "strip-only" basis. It looks at your .ts file and essentially says, "I don't care about your interfaces, your types, or your generics. I'm just going to delete all the TypeScript-specific syntax and leave the JavaScript behind."
In the example above, esbuild just deleted : User from the function argument. It didn't check if user.id actually existed on the User interface because doing that requires a full type-graph analysis, which is exactly what makes tsc slow. You traded safety for speed, and you didn't realize the safety net was gone.
Implementing the Parallel Pipeline
You don't have to go back to the slow tsc builds, but you cannot rely on esbuild or SWC for correctness. The industry standard is to split your pipeline into two separate concerns: Transpilation (fast) and Type Checking (thorough).
I usually set this up in the package.json scripts. Instead of one "build" command, I create a "check" command and a "build" command. The "check" command uses tsc with the --noEmit flag, which tells TypeScript to validate the types but not to actually generate any JavaScript files.
{
"scripts": {
"type-check": "tsc --noEmit",
"build": "esbuild src/index.ts --bundle --outfile=dist/bundle.js",
"dev": "concurrently \"npm run type-check -- --watch\" \"npm run build -- --watch\""
}
}
By doing this, you get the best of both worlds. Your development server restarts in milliseconds because esbuild is handling the JS generation, but you still have a terminal window open running tsc --noEmit that will alert you the second you introduce a type mismatch. If you're setting up a CI/CD pipeline, your build should fail if npm run type-check fails, even if the esbuild step succeeded.
Choosing Between esbuild and SWC
You'll see both of these mentioned everywhere. For 90% of projects, they are interchangeable, but there are some nuances I've noticed over the years.
- esbuild: Written in Go. It is incredibly fast and has a fantastic API for bundling. If you need to bundle multiple files into one single JS file for the browser, esbuild is usually the smoother experience.
- SWC: Written in Rust. It's often used as a drop-in replacement for Babel (especially in Next.js). If you need complex transformations—like heavily customized decorators or legacy browser support through polyfills—SWC tends to have a more robust plugin ecosystem.
My rule of thumb? Start with esbuild for its simplicity and bundling capabilities. If you find yourself needing deep AST transformations that esbuild can't handle, move to SWC.
📋 Practical Task
Configure a Dual-Pipeline Build for a User Profile API
You have a small TypeScript project with a src/index.ts file and a tsconfig.json. Currently, the project uses tsc for everything, and the build is becoming sluggish.
Your Goal: Modify the package.json to implement the "Parallel Pipeline" strategy using esbuild.
Requirements:
- Create a script named
"check"that runs the TypeScript compiler to validate types without producing output files. - Create a script named
"build"that usesesbuildto bundlesrc/index.tsinto a single file located atdist/main.js. - Create a script named
"validate"that runs both the check and the build sequentially (using&&), ensuring that the build only happens if the type check passes.
Note: Assume typescript and esbuild are already installed as devDependencies.
There are no comments for now.