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
56: Debugging TypeScript with Source Maps
You’ve probably noticed by now that browsers and Node.js have no idea what a .ts file is. They only speak JavaScript. This means that whenever we run our code, we're actually executing the compiled output. For a while, that's fine. But the moment you hit a runtime error, the browser's console points you to a line number in a .js file that looks nothing like the code you actually wrote.
That's where source maps come in. Think of a source map as a dictionary that tells the debugger, "Line 12, Column 4 of app.js actually corresponds to Line 5, Column 2 of app.ts."
Creating a buggy validator
Let's build a small user profile validator. I want to make sure users have a valid age and a non-empty username. I'll intentionally leave a logic bug in here so we have something to actually debug.
// userValidator.ts
interface User {
username: string;
age: number;
}
export function validateUser(user: User) {
console.log("Validating user:", user.username);
if (user.username.length === 0) {
throw new Error("Username cannot be empty");
}
// Logic bug: I accidentally used '>' instead of '<'
// and I'm accessing a property that might be undefined
if (user.age > 150) {
console.log("That's a very old user!");
}
return true;
}
const testUser = { username: "dev_mentor", age: -5 };
validateUser(testUser);
The frustration of debugging compiled JS
If I compile this with a basic tsc command and run it in a browser or Node, and then I look at the stack trace when it crashes (or when I set a breakpoint), I'm staring at the .js file. If I'm using a minifier or a bundler, it's even worse—I'm debugging a single line of mangled code like function a(b){return b.u.length...}. It's a nightmare.
Bridging the gap with sourceMap: true
To fix this, we need to tell the TypeScript compiler to generate these map files. I'll head over to my tsconfig.json and flip the switch.
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"sourceMap": true, // This is the magic line
"outDir": "./dist"
}
}
Now, when I run tsc, I'll notice that for every .js file in my dist folder, there's a corresponding .js.map file. These are the "dictionaries" I mentioned earlier.
Wait, why isn't it working?
Here is where I usually trip up when I'm setting up a new project. I ran the compiler, I saw the .map files, but when I opened Chrome DevTools, I still saw the JavaScript file. I spent five minutes wondering if my tsconfig was being ignored.
The mistake? I had "Enable JavaScript source maps" unchecked in my browser's DevTools settings. It's a small toggle, but it's the difference between the browser ignoring the map files or actually using them to reconstruct the original TypeScript source in the "Sources" tab. Always check your environment settings before you start blaming the compiler!
Finally seeing the TypeScript source
With the settings corrected, the experience changes completely. I can now open the "Sources" tab in the browser, find userValidator.ts (the actual TypeScript file!), and click on line 15 to set a breakpoint.
When the code hits that breakpoint, the debugger pauses on the TypeScript line. I can hover over the user object and see that age is -5. Because I'm looking at the source map, I can immediately see that my check if (user.age > 150) is useless for catching negative numbers. I can fix the logic in my .ts file, recompile, and I'm back in business without ever having to manually hunt through a compiled JavaScript file.
📋 Practical Task
Fixing the Shopping Cart Totalizer
You have been given a small project with a bug in the price calculation logic. The project is already configured to generate source maps, but the logic is failing in production.
Your Task:
- Open the project and run the build command to generate the
/distfolder. - Launch the application in your browser and open the Developer Tools.
- Navigate to the "Sources" tab and locate the TypeScript file
cartLogic.ts(do not debug the.jsfile). - Set a breakpoint inside the
calculateTotalfunction. - Refresh the page and inspect the
itemobject to find why the total is returningNaN. - Fix the bug in
cartLogic.ts, recompile, and verify the total is now calculating correctly.
There are no comments for now.