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
142: Optional Properties Revisited
You've probably used the ? modifier a hundred times by now, but there's a subtle trap that still trips me up when I'm rushing through a PR: the gap between a property being optional in the type system and how it actually behaves at runtime.
interface UserProfile {
firstName: string;
lastName: string;
middleName?: string; // Optional!
}
function getFullDisplayName(user: UserProfile) {
// I'm just building a simple string here...
return `${user.firstName} ${user.middleName} ${user.lastName}`;
}
const userA = { firstName: "Jane", lastName: "Doe" };
console.log(getFullDisplayName(userA));
// Output: "Jane undefined Doe"
The "undefined" string leak
If you're using a strict TypeScript configuration, the compiler might actually let this slide depending on your noImplicitAny and strictNullChecks settings, but the runtime result is a disaster. JavaScript doesn't "skip" the property just because it's missing; it evaluates the missing property as undefined and then happily coerces that into the string "undefined".
I've seen this happen in production logs more times than I'd like to admit. The developer thinks, "It's optional, so if it's not there, it just isn't there." But in the eyes of a template literal, undefined is a value that needs to be represented.
Cleaning up with nullish coalescing and filtering
The fix isn't just about silencing a warning; it's about deciding what the "fallback" behavior should be. If you just want to omit the middle name when it's missing, you can't just rely on the ? in the interface. You have to handle the absence of the value explicitly.
function getFullDisplayName(user: UserProfile) {
// Option 1: The quick fix with nullish coalescing
// This still leaves an extra space if middleName is missing.
// return `${user.firstName} ${user.middleName ?? ''} ${user.lastName}`;
// Option 2: The professional approach
// Filter out the falsy/undefined values and join them with a space.
return [user.firstName, user.middleName, user.lastName]
.filter(Boolean)
.join(' ');
}
const userA = { firstName: "Jane", lastName: "Doe" };
console.log(getFullDisplayName(userA)); // Output: "Jane Doe"
I prefer the array approach here. Why? Because it scales. If you suddenly decide to add prefix (like "Dr.") or suffix (like "III") as optional properties, you don't have to rewrite a complex chain of ternary operators or worry about double-spaces appearing in your UI. You just add them to the array.
One quick side note: Be careful using .filter(Boolean) if 0 or false are valid values you want to keep. In the case of names, it's perfect. In the case of a score?: number property, Boolean(0) is false, and your zero would vanish. In those cases, use a more explicit check: .filter(val => val !== undefined).
📋 Practical Task
Refactoring the Product Discount Calculator
You are working on an e-commerce checkout system. The Product interface has several optional properties. Currently, the calculateFinalPrice function is producing NaN or weird results because it doesn't properly handle the optional properties during math operations.
Your Task: Fix the calculateFinalPrice function so that it handles missing discount and taxRate values without crashing or returning NaN. Assume a default tax rate of 0 if none is provided, and a discount of 0 if none is provided.
interface Product {
name: string;
basePrice: number;
discount?: number; // Percentage, e.g., 0.1 for 10%
taxRate?: number; // Percentage, e.g., 0.05 for 5%
}
function calculateFinalPrice(product: Product): string {
// BUGGY CODE:
const priceAfterDiscount = product.basePrice * (1 - product.discount);
const finalPrice = priceAfterDiscount * (1 + product.taxRate);
return `Total: $${finalPrice.toFixed(2)}`;
}
// Test Case 1: Should be $100.00
const item1: Product = { name: "Plain T-Shirt", basePrice: 100 };
// Test Case 2: Should be $94.50 (100 - 10% discount + 5% tax on 90)
const item2: Product = {
name: "Fancy T-Shirt",
basePrice: 100,
discount: 0.1,
taxRate: 0.05
};
console.log(calculateFinalPrice(item1));
console.log(calculateFinalPrice(item2));
There are no comments for now.