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

You've likely seen the object type (lowercase 'o') and thought, "Perfect, that's exactly what this is—an object." It feels intuitive. If you're writing a function that takes a configuration object or a set of metadata, object seems like the most honest description of that data. But in TypeScript, object is a bit of a trap. It doesn't mean "an object with properties"; it actually means "anything that isn't a primitive."

The Trap of the Lowercase Object

Let's look at a scenario I've run into a dozen times. Imagine you're building a logging utility that accepts a message and some optional metadata. You want to make sure the metadata isn't just a random string or a number—it has to be a proper object.

function logEvent(message: string, metadata: object) {
  console.log(`Event: ${message}`);
  // I just want to check if there's a timestamp in here
  if (metadata.timestamp) { 
    console.log(`Occurred at: ${metadata.timestamp}`);
  }
}

If you try to run this, TypeScript is going to scream at you. It'll tell you that Property 'timestamp' does not exist on type 'object'. This is where the frustration kicks in. You're looking at the code thinking, "But it IS an object! Why can't I just access a property?"

The reason is that object is a "non-primitive" type. It tells TypeScript that the value isn't a string, number, boolean, symbol, null, or undefined. However, it tells TypeScript absolutely nothing about the members of that object. Since TypeScript can't guarantee that timestamp exists on every single possible non-primitive in existence, it blocks you from accessing it for your own safety.

When You Actually Need to Touch the Data

If you're in a position where you actually need to read properties from the object, object is the wrong tool. Depending on how much you know about the data, you have two better paths. If you know the exact shape, use an interface. But if you're dealing with dynamic data—like a JSON response where the keys vary—you want a Record.

Here is how I'd rewrite that logger to actually be useful:

function logEvent(message: string, metadata: Record<string, unknown>) {
  console.log(`Event: ${message}`);
  
  // Now TS knows it's an object with string keys, 
  // but it doesn't assume what the values are (unknown)
  if (metadata.timestamp) {
    console.log(`Occurred at: ${metadata.timestamp}`);
  }
}

By using Record<string, unknown>, you're telling the compiler: "This is an object, and I can index into it using strings." The unknown part is the secret sauce here. It's safer than any because it forces you to perform type checking or casting before you do anything complex with the value, but it still allows you to access the property in the first place.

Picking the Right Tool

So, when should you actually use object? In my experience, it's rare, but it's useful when you specifically want to prevent primitives. For example, if you're writing a library function that handles object references (like a deep-clone utility or a comparison tool), you don't care what the properties are; you just need to ensure the input isn't a string or a number. In those cases, object is exactly what you want because it explicitly forbids primitives without making any promises about the internal structure.




📋 Practical Task

Refactoring the API Response Validator

You have been handed a piece of code for a response validator. The original author used object for the payload, but the function is failing to compile because it tries to access the status property of that payload.

Refactor the validateResponse function below. Replace the object type with a more appropriate TypeScript type (like a Record) that allows the code to access the status property while maintaining type safety for the values.

function validateResponse(payload: object) {
  if (payload.status === 'success') {
    return true;
  }
  return false;
}

// Test cases
validateResponse({ status: 'success', code: 200 }); // Should work
validateResponse({ status: 'error', code: 500 });   // Should work
Rating
0 0

There are no comments for now.

to be the first to leave a comment.