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
53: Strict Mode and Compiler Flags
I want to show you something that usually happens about three months into a professional TypeScript project. You start with a configuration that feels "easy," everything compiles, and you're moving fast. Then, one Tuesday, you get a production error—a TypeError: Cannot read property 'toLowerCase' of undefined—and you realize that TypeScript was actually letting you write dangerous code all along.
Living in the Danger Zone
Let's look at a piece of code I wrote for a user profile system. I've got a simple interface and a function to format a user's display name. Right now, my tsconfig.json is pretty relaxed—I haven't touched the strict flags yet.
interface UserProfile {
username: string;
displayName?: string; // Optional
}
function formatDisplayName(user: UserProfile) {
// I'm assuming if displayName is missing, I'll just use username
const name = user.displayName || user.username;
return name.trim().toUpperCase();
}
const userA = { username: "jdoe123", displayName: "John Doe" };
const userB = { username: "smith_99" };
console.log(formatDisplayName(userA)); // "JOHN DOE"
console.log(formatDisplayName(userB)); // "SMITH_99"
This looks fine, right? It compiles without a single red squiggly line. But here's the problem: I'm playing a guessing game. I'm trusting that name will always be a string. If for some bizarre reason user.username was also undefined (maybe due to a bad API response), the whole app crashes. TypeScript isn't warning me because, by default, it's being "nice."
Flipping the Switch
Now, let's stop being nice. I'm going into my tsconfig.json and adding one single line: "strict": true.
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "CommonJS"
}
}
The moment I save this file, my editor explodes with errors. Suddenly, the formatDisplayName function is underlined in red. Specifically, name.trim() is complaining. Why? Because strict: true is actually a bundle of flags. The big one hitting me here is strictNullChecks.
TypeScript is now telling me: "Hey, you said displayName is optional. Even though you have a fallback to username, what if both are missing? You can't call .trim() on something that might be undefined."
Wrestling with Nulls
I can't just ignore this. I have to prove to the compiler that I've handled the empty case. I'll try a type guard first to see if that satisfies the compiler.
function formatDisplayName(user: UserProfile) {
const name = user.displayName || user.username;
if (!name) {
return "Anonymous";
}
// Now TypeScript knows 'name' cannot be null or undefined here
return name.trim().toUpperCase();
}
That fixed it. By adding that if (!name) check, I've "narrowed" the type. I've essentially told TypeScript: "I've handled the scary stuff, you can relax now." This is the core philosophy of strict mode: it forces you to handle the edge cases before they happen in production.
Closing the 'Any' Loophole
While I'm in here, let's look at another flag included in the strict bundle: noImplicitAny. I'll add a quick helper function to my code to see how it behaves.
function logUser(user, message) {
console.log(`${message}: ${user.username}`);
}
Without strict mode, TypeScript just assumes user and message are of type any. It doesn't complain, but it also doesn't help me. With strict: true, this is now an error. It's telling me that it can't infer the type, and since I didn't provide one, it's refusing to guess.
I could just type them as any to make the error go away, but that's cheating. The whole point of using TypeScript is to avoid any. I'll give them proper types instead:
function logUser(user: UserProfile, message: string) {
console.log(`${message}: ${user.username}`);
}
It feels like more work upfront, but I've just eliminated two potential bugs: passing a number instead of a string for the message, and passing an object that doesn't actually have a username property. When you turn on strict mode, you stop treating TypeScript as a suggestion and start treating it as a contract.
📋 Practical Task
Hardening the Product Discount Calculator
You have been handed a legacy piece of code for an e-commerce store. The current tsconfig.json has "strict": false, and the code is riddled with potential runtime crashes. Your goal is to harden this code.
Your Task:
- Assume
"strict": trueis now enabled in the compiler settings. - Fix the
calculateDiscountfunction below so that it compiles without errors. - You must handle the case where
discountRateis missing (default it to 0). - You must ensure
priceis a valid number before performing calculations. - Remove any implicit
anytypes by explicitly defining the types for the function parameters.
interface Product {
name: string;
price?: number;
discountRate?: number;
}
function calculateDiscount(product, label) {
// TODO: Fix this function to work under "strict": true
const finalPrice = product.price * (1 - product.discountRate);
return `${label}: ${finalPrice}`;
}
const laptop = { name: "Gaming Laptop", price: 1200, discountRate: 0.1 };
const mouse = { name: "Wireless Mouse" }; // Missing price and discountRate
console.log(calculateDiscount(laptop, "Sale Price"));
console.log(calculateDiscount(mouse, "Sale Price"));
There are no comments for now.