Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

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:

  1. Define a type GameItem that requires an id (string, read-only) and a weight (number). Add an optional property called description (string).
  2. Create a function called calculateTotalWeight that takes an array of GameItem and returns the sum of their weights.
  3. Create two specific objects:
    • A magicSword that has an id, weight, and an extra property damage (number).
    • A healthPotion that has an id, weight, and an extra property healAmount (number).
  4. Pass an array containing both the magicSword and healthPotion into the calculateTotalWeight function to verify that structural typing allows objects with extra properties to be treated as GameItems.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.