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
145: Excess Property Checks
I want to show you something that trips up almost every developer when they start getting comfortable with TypeScript's structural typing. You know the rule: as long as an object has the required properties, TypeScript is usually happy, even if it has a few extra ones. But there's a specific scenario where TypeScript suddenly becomes a strict bouncer at the door.
Wait, why is this an error?
Let's look at a simple scenario. I'm building a system to handle user profiles, and I have a function that just prints out a welcome message based on a user's name and email.
interface User {
name: string;
email: string;
}
function welcomeUser(user: User) {
console.log(`Welcome, ${user.name}! We'll contact you at ${user.email}.`);
}
Now, I've got a user object that has a bit more data than the interface requires—maybe it came from a database and includes their age. I'll try to pass it directly into the function:
welcomeUser({
name: "Alice",
email: "alice@example.com",
age: 30 // Error: Object literal may only specify known properties
});
This is where I usually stop and blink. I'm thinking, "Wait, the User interface requires a name and an email. I provided both. Who cares if I also provided the age?" According to everything we've learned about structural typing, this should be fine. But TypeScript is throwing a red squiggly line under age.
The loophole
I suspect this is just a specific check for object literals. To test this, I'll try to "hide" the object in a variable first. Instead of passing the object directly into the function call, I'll assign it to a constant and then pass that constant.
const userWithAge = {
name: "Alice",
email: "alice@example.com",
age: 30
};
welcomeUser(userWithAge); // No error!
Now it works. No errors, no warnings. The exact same data is being passed into the exact same function, but because I moved the object into a variable first, TypeScript stopped complaining. This feels like a bug or a weird inconsistency, but it's actually a very intentional feature called "Excess Property Checks."
The logic behind the madness
Think about why TypeScript does this. When you pass an object literal directly into a function, you are explicitly saying, "I am creating this object specifically for this purpose."
If I accidentally typed emial instead of email, and TypeScript just ignored the extra property, I'd be passing an object that's missing a required field while carrying a useless typo. By being strict with object literals, TypeScript is essentially saying: "You're creating this on the fly; are you sure you didn't make a typo?"
However, when you use a variable, TypeScript assumes the object might have been created elsewhere—perhaps by a library or a API response—and as long as it satisfies the minimum requirements of the User interface, it's safe to use. It prioritizes flexibility over strictness once the object exists independently of the function call.
My rule of thumb? If you're seeing this error, it's usually a signal to double-check your spelling. If you actually want the object to have extra properties that the function doesn't care about, you can either use the variable approach I used above, or you can use a type assertion like as User, though I generally prefer the variable route because it's cleaner.
📋 Practical Task
Refactoring the Product Inventory Guard
You are working on an e-commerce tool. You have a Product interface and a function logProductDetails that expects it. However, your team is currently passing "raw" product objects from a JSON file directly into the function, and TypeScript is blocking the build because the JSON objects contain extra metadata (like internalId and warehouseLocation) that aren't in the Product interface.
Your Task:
- Create a
Productinterface withtitle(string) andprice(number). - Create a function
logProductDetails(product: Product)that logs the title and price. - Attempt to call
logProductDetailsusing an object literal that includestitle,price, andinternalId. Observe the error. - Fix the error by implementing the "variable loophole" discussed in the lesson, ensuring the object is assigned to a constant before being passed to the function.
There are no comments for now.