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
10: Type Inference
I want to show you something that usually happens in the background of your editor, but once you realize it's happening, it changes how you write TypeScript. Let's look at a piece of a pricing module I'm sketching out for a client.
let productPrice = 49.99;
let productName = "Mechanical Keyboard";
The invisible labels
If you're looking at this and thinking, "Wait, you didn't actually define the types here," you're right. I didn't. But if you hover your mouse over productPrice in your IDE, you'll see TypeScript has already labeled it as a number. Same for productName—it's a string.
This is Type Inference. TypeScript is essentially saying: "I see you assigned a number to this variable, so I'm going to assume it's always supposed to be a number." It saves us from the tediousness of writing : number or : string on every single line of our code. I personally find that over-annotating simple variables just creates visual noise that makes the logic harder to read.
Testing the guardrails
Now, let's see where this becomes useful. I'm feeling lazy, and I decide that if a product is out of stock, I'll just set the price to a string saying "Out of Stock". Let's try that:
let productPrice = 49.99;
// ... some logic later ...
productPrice = "Out of Stock";
Immediately, TypeScript throws a red squiggly line under "Out of Stock". The error tells us that a string cannot be assigned to a variable of type number. Even though I never explicitly told TypeScript that productPrice was a number, it remembered the initial assignment and is now protecting me from a potential runtime bug. If this were plain JavaScript, the code would run fine, but then my calculateTax() function would probably crash when it tries to multiply "Out of Stock" by 0.07.
When inference gets a bit too "helpful"
Inference is great until it makes a guess that's too broad. Let's try creating a list of product IDs. I'll start with a few numbers, but then I'll accidentally throw in a string.
let productIds = [101, 102, "103"];
If you check the type of productIds now, TypeScript hasn't called it a number[]. Instead, it's inferred as (string | number)[]. It saw a mix of types and decided to create a union type to accommodate everything.
This is a "Best Common Type" scenario. TypeScript is trying its best to be inclusive, but in a real app, having a product ID that could be either a string or a number is usually a recipe for disaster. This is where I stop relying on inference and step in to be explicit:
let productIds: number[] = [101, 102, "103"];
// Now TS screams at "103" specifically, which is exactly what I want.
Following the trail in functions
Finally, let's look at how inference flows through functions. I'll write a quick helper to apply a discount:
function applyDiscount(price: number, discount: number) {
return price - discount;
}
const finalPrice = applyDiscount(100, 10);
Notice I didn't specify a return type for applyDiscount. However, because TypeScript knows that price and discount are numbers, and it knows that subtracting a number from a number always results in a number, it automatically infers the return type of the function as number. Consequently, it also knows that finalPrice is a number.
I usually let TypeScript infer return types for simple functions. It keeps the code clean. But if the function is complex—maybe it returns a deeply nested object or a union of different types—I'll add the return type explicitly. It acts as a contract, ensuring I don't accidentally return something I didn't intend to.
📋 Practical Task
Fixing the Inventory Stock Tracker
You've inherited a small piece of code used to track warehouse stock. The original developer relied too heavily on type inference, and now the code is allowing invalid data to slip through, which is causing crashes in the shipping module.
Your task: Look at the code below. Identify where type inference is creating a "too broad" type (a union type) and apply an explicit type annotation to force the variable to only accept numbers. Then, fix the invalid assignment that the compiler should now be flagging.
// Current broken code:
let currentStock = [50, 120, "Unknown", 30];
function updateStock(amount: number) {
// This logic is failing because currentStock might contain strings
return currentStock[0] + amount;
}
const newTotal = updateStock(10);
console.log(newTotal);
Requirements:
- Change the
currentStockdeclaration so it is explicitly an array of numbers. - Correct the
"Unknown"value to a valid number so the code compiles. - Verify that
newTotalis correctly inferred as a number.
There are no comments for now.