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
13: Object Types
I see this a lot when people move from languages like Java or C# to TypeScript: they assume that if they define an object type, they are creating a rigid "mold" that the object must fit exactly. They think that if a type defines three properties, an object with four properties is an invalid match.
// The misconception in action
type User = {
id: string;
username: string;
};
function greet(user: User) {
console.log(`Hello, ${user.username}!`);
}
const admin = {
id: "admin-01",
username: "super_user",
permissions: ["all"], // Extra property!
};
greet(admin); // You might expect this to error, but it doesn't.
If you expected that greet(admin) would throw a type error because permissions isn't part of the User type, you're thinking in terms of nominal typing. TypeScript doesn't work that way. This leads us to the core of how object types actually function.
Stop Thinking in Blueprints; Start Thinking in Shapes
TypeScript uses something called structural typing. In plain English: if it looks like a duck and quacks like a duck, it's a duck. When you define an object type, you aren't telling TypeScript "this object must be exactly this," you're saying "this object must have at least these properties."
In the example above, the greet function doesn't care that the admin object has a permissions array. It only cares that it can find an id and a username. Since those exist, the "shape" is compatible. I actually find this incredibly powerful once you get used to it—it allows you to create very flexible APIs without having to build deep, complex inheritance hierarchies.
The "Excess Property" Gotcha
Now, here is where it gets confusing. You might have tried passing an object literal directly into a function and seen an error, even though it felt like it should work. Look at this:
greet({
id: "user-02",
username: "dev_jane",
email: "jane@example.com" // Error: Object literal may only specify known properties
});
Wait, why did this fail when the admin variable worked? This is called Excess Property Checking. TypeScript assumes that if you are creating a brand new object literal on the fly and adding a property that isn't in the type, you've probably made a typo. It's a safety feature to prevent you from typing usernamee instead of username. Once that object is assigned to a variable (like we did with admin), that strict check disappears and structural typing takes over.
Handling Optionality and Read-Only Data
In the real world, not every object has every piece of data. You don't want to force a user to provide a middle name if they don't have one. We handle this with the ? modifier.
type Product = {
sku: string;
price: number;
description?: string; // This is optional
};
const simpleProduct: Product = {
sku: "BOOK-123",
price: 19.99
// No description? No problem.
};
I also highly recommend using readonly for properties that should never change after the object is created. If you're dealing with a configuration object or a database ID, mark it. It saves you from those annoying bugs where a helper function accidentally mutates a global setting.
type Config = {
readonly apiKey: string;
timeout: number;
};
const myConfig: Config = {
apiKey: "secret-123",
timeout: 5000
};
myConfig.timeout = 3000; // Allowed
myConfig.apiKey = "new-key"; // Error: Cannot assign to 'apiKey' because it is a read-only property.
📋 Practical Task
Implementing a Game Character Inventory System
You are building a simple inventory system for an RPG. You need to ensure that your functions can handle different types of items (weapons, potions, armor) as long as they meet the minimum requirements of a "Game Item."
Your Task:
- Define a type
GameItemthat requires anid(string, read-only) and aweight(number). Add an optional property calleddescription(string). - Create a function called
calculateTotalWeightthat takes an array ofGameItemand returns the sum of their weights. - Create two specific objects:
- A
magicSwordthat has anid,weight, and an extra propertydamage(number). - A
healthPotionthat has anid,weight, and an extra propertyhealAmount(number).
- A
- Pass an array containing both the
magicSwordandhealthPotioninto thecalculateTotalWeightfunction to verify that structural typing allows objects with extra properties to be treated asGameItems.
There are no comments for now.