Object Types

Difficulty: Intermediate

Object types are one of the most frequently used type constructs in TypeScript. They describe the shape of an object - what properties it has and what types those properties hold. The simplest form is an inline object type written directly in a type annotation: `{ name: string; age: number }`. Every property is declared with its name, a colon, and its type, separated by semicolons or commas.

Optional properties are marked with a `?` after the property name: `{ name: string; age?: number }`. An optional property can be omitted entirely when creating the object, and its type becomes a union with `undefined`. This is essential for modeling real-world data where not all fields are always present - API responses, form data, configuration objects, and user preferences commonly have optional fields.

The `readonly` modifier prevents a property from being reassigned after the object is created: `{ readonly id: number; name: string }`. This is a compile-time check only - it doesn't affect runtime behavior. Readonly properties are useful for identifiers, timestamps, and any data that should be set once and never modified. Note that `readonly` is shallow: if a readonly property holds an object, the nested object's properties can still be modified unless they are also marked readonly.

TypeScript uses structural typing for objects, meaning two objects are compatible if they have the same shape, regardless of how they were declared. If a function expects `{ name: string }`, you can pass any object that has a `name` property of type `string`, even if it has additional properties. This is fundamentally different from nominal typing (used in Java/C#) where objects must be explicitly declared as implementing an interface.

Code examples

Inline object types and optional properties

function createProfile(info: {
  name: string;
  email: string;
  bio?: string;
  age?: number;
}): string {
  let profile = `${info.name} <${info.email}>`;
  if (info.bio) {
    profile += ` - ${info.bio}`;
  }
  if (info.age !== undefined) {
    profile += ` (age ${info.age})`;
  }
  return profile;
}

console.log(createProfile({ name: "Alice", email: "alice@mail.com" }));
console.log(createProfile({ name: "Bob", email: "bob@mail.com", bio: "Developer", age: 28 }));

Optional properties are marked with `?`. When omitted, their value is `undefined`. Always check for undefined before using optional properties.

Readonly properties

function createUser(name: string, email: string): { readonly id: number; readonly createdAt: string; name: string; email: string } {
  return {
    id: Math.floor(Math.random() * 10000),
    createdAt: new Date().toISOString(),
    name,
    email
  };
}

const user = createUser("Alice", "alice@mail.com");
// user.id = 999;         // Error: Cannot assign to 'id' because it is a read-only property
// user.createdAt = "";   // Error: Cannot assign to 'createdAt' because it is a read-only property
user.name = "Alice Smith"; // OK: name is not readonly

console.log(`${user.name} (id: ${user.id})`);
console.log(`Name updated: ${user.name}`);

Readonly properties cannot be reassigned after creation. Use readonly for IDs, timestamps, and any value that should be immutable. Mutable properties like name can still be updated.

Nested object types

function formatAddress(location: {
  street: string;
  city: string;
  state: string;
  zip: string;
  coordinates?: { lat: number; lng: number };
}): string {
  let result = `${location.street}, ${location.city}, ${location.state} ${location.zip}`;
  if (location.coordinates) {
    result += ` (${location.coordinates.lat}, ${location.coordinates.lng})`;
  }
  return result;
}

console.log(formatAddress({
  street: "123 Main St",
  city: "Springfield",
  state: "IL",
  zip: "62701"
}));
console.log(formatAddress({
  street: "456 Oak Ave",
  city: "Portland",
  state: "OR",
  zip: "97201",
  coordinates: { lat: 45.52, lng: -122.68 }
}));

Object types can be nested - an optional `coordinates` property contains its own object type with lat and lng. Check the outer optional before accessing nested properties.

Key points

Concepts covered

Inline object types, Optional properties, Readonly properties, Nested objects