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
57: Type-Checking Only Builds
A few years ago, I was lead on a project using a modern build tool that handled the TypeScript-to-JavaScript conversion incredibly fast. We were thrilled with the developer experience—saves were nearly instantaneous. One afternoon, a junior dev pushed a change that accidentally changed a User object's id from a string to a number in one specific utility file. The build pipeline finished in seconds, the tests passed (because they were mocking the data), and the code hit production. Within ten minutes, the dashboard started crashing for every user because the frontend was trying to call .toLowerCase() on a number. The terrifying part? The code had blatant TypeScript errors that would have caught this, but because our build tool just "stripped" the types to get to JS faster, it never actually checked if the types were correct.
The Speed Trap of Type Stripping
If you're using tools like Vite, esbuild, or SWC, you've probably noticed that your project builds almost instantly. This is because these tools perform "transpilation only." They don't actually look at your types; they just delete them and hand the resulting JavaScript to the browser. While this is great for productivity, it creates a dangerous blind spot: your code can be completely broken from a type perspective, but it will still "build" and run.
To solve this, we use what I call a "Type-Checking Only Build." This is where we tell the official TypeScript compiler (tsc) to analyze the entire project for errors, but specifically tell it not to generate any JavaScript files. We use the --noEmit flag for this. It's the equivalent of asking TypeScript, "Don't give me any files, just tell me if I've messed anything up."
# Run this in your terminal to check for errors without creating .js files
npx tsc --noEmit
Integrating Safety into your Workflow
Running a manual command every few minutes is a chore you won't stick to. I usually recommend adding this to your package.json scripts so it becomes a first-class citizen of your development cycle. I personally like to call this script type-check.
{
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"type-check": "tsc --noEmit"
}
}
Notice how I've updated the build script above. By chaining tsc --noEmit before the actual build command with &&, the process will hard-fail if there's a single type error. This ensures that no code ever reaches your staging or production environment unless it is type-safe. It turns your type system from a "helpful suggestion" into a "strict requirement."
One thing to keep in mind: tsc --noEmit can be slower than your Vite build because it's doing the heavy lifting of analyzing the entire dependency graph. Don't let that discourage you. I'd rather wait an extra thirty seconds in CI than spend three hours on a Saturday morning fixing a production outage caused by a null pointer that TypeScript saw coming a mile away.
📋 Practical Task
Exercise: Hardening the Order-Processing Pipeline
You have been handed a project that uses a fast-transpiler build. The project currently "builds" successfully, but it contains a critical type mismatch in the order processing logic that would cause a runtime crash.
- Open the project's
package.jsonand create atype-checkscript that runs the TypeScript compiler without emitting files. - Run your new script. You will notice that while the project "runs,"
tscreports an error insrc/orders.tswhere aPrice(number) is being treated as aCurrencyString(string). - Fix the type error in
src/orders.tsso that the logic is sound. - Run the
type-checkscript again to verify that the build now passes the type-checking phase.
There are no comments for now.