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
231: Parameter Destructuring with Types
I see this mistake all the time when developers transition from vanilla JavaScript to TypeScript. You're used to the clean look of destructuring your function arguments, and you try to apply types the way you would for a standard variable. It looks intuitive, but it actually creates a subtle, frustrating bug.
type UserProfile = {
username: string;
email: string;
isAdmin: boolean;
};
// ❌ This is broken, but it looks correct at first glance
function updatePermissions({ username: string, isAdmin: boolean }) {
console.log(`Updating ${username} to admin status: ${isAdmin}`);
}
updatePermissions({ username: "alice", email: "alice@example.com", isAdmin: true });
The Alias Trap
If you try to run the code above, TypeScript won't throw an error where you'd expect it to. Instead, it'll complain that username and isAdmin are used before being assigned. Why? Because in JavaScript, the syntax { key: value } inside a function parameter isn't for typing—it's for renaming.
When you write { username: string }, you aren't telling TypeScript that username is a string. You're telling JavaScript: "Take the property username from the incoming object and assign its value to a new local variable called string."
I've spent way too many hours debugging "undefined" variables only to realize I had accidentally renamed my parameters to string or number. It's a rite of passage, but we can fix it easily.
Typing the Object Shape
To fix this, you have to separate the destructuring (the logic of pulling values out) from the typing (the definition of what those values are). You type the entire object argument, not the individual properties inside the curly braces.
type UserProfile = {
username: string;
email: string;
isAdmin: boolean;
};
// ✅ The type is applied to the whole object, then we destructure it
function updatePermissions({ username, isAdmin }: UserProfile) {
console.log(`Updating ${username} to admin status: ${isAdmin}`);
}
Now, TypeScript knows that the argument being passed in must match the UserProfile shape. Inside the function body, username is correctly inferred as a string and isAdmin as a boolean.
Handling Inline Destructuring
Sometimes, you don't want to create a named type or interface for a small helper function. You can still do this inline, but the syntax is a bit clunkier because you have to define the object structure immediately after the destructuring pattern.
function logCoordinates({ x, y }: { x: number; y: number }) {
console.log(`Point is at ${x}, ${y}`);
}
It feels a bit repetitive to see x and y twice, but this is the only way to ensure TypeScript understands you're defining the shape of the incoming object rather than renaming its properties. If the object gets any larger than two or three properties, I strongly recommend moving that type definition to its own line or a separate type alias to keep your function signatures readable.
📋 Practical Task
Fixing the Notification Settings Handler
You've been handed a piece of code for a notification system, but the previous developer fell into the "Alias Trap." The sendNotification function is currently attempting to rename the userId and message parameters instead of typing them, causing the function to fail.
Your Task: Fix the sendNotification function so that it correctly destructures and types the NotificationRequest object. Ensure the function can access userId and message as the correct types without renaming them.
type NotificationRequest = {
userId: string;
message: string;
priority: 'low' | 'high';
};
// FIX THIS FUNCTION
function sendNotification({ userId: string, message: string, priority: string }) {
console.log(`Sending ${priority} priority message to ${userId}: ${message}`);
}
const request: NotificationRequest = {
userId: "user_123",
message: "Your order has shipped!",
priority: "high"
};
sendNotification(request);
There are no comments for now.