Object Types in TypeScript: Shaping Your Data with Confidence

Learn how to type objects in TypeScript — inline shapes, type aliases and interfaces, nested objects, optional and readonly properties, and index signatures for dynamic keys.

Published September 20, 20269 min readBy ACY Partner Indonesia
Object Types cover with a TypeScript object shape
300 × 250Ad Space AvailablePlace your ad here

Most of the data you handle in real software is not a lonely number or a single piece of text. It is a bundle: a user has a name, an email, and an age, all travelling together. In JavaScript we wrap that bundle in an object. In TypeScript, we get to describe the shape of that object — exactly which properties it has and what type each one holds — so the editor and the compiler can catch mistakes before your code ever runs.

This guide walks you through every common way to type an object, starting from the simplest inline shape and building up to reusable names, nested structures, optional and read-only properties, and even objects whose keys you don’t know in advance. No prior TypeScript depth is assumed — just bring a little curiosity.

What an object type actually is

An object type is a description. It tells TypeScript, “any value I label with this type must have these properties, with these types.” Here is the most direct form, written right where the variable lives:

let user: { name: string; age: number } = {
  name: "Jane Doe",
  age: 32,
};

The part in the curly braces — { name: string; age: number } — is the type. It says the object must have a name that is a string and an age that is a number. If you forget age, or spell name as naem, TypeScript complains immediately.

This inline style is handy for a quick, one-off shape. But the moment you want to use the same shape in more than one place, repeating it everywhere becomes tiring and error-prone. That is where named types come in.

Naming a shape: type aliases and interfaces

TypeScript gives you two tools to put a name on an object shape: the type alias and the interface. Both let you write the shape once and reuse the name.

A type alias uses the type keyword and an = sign:

type User = {
  name: string;
  age: number;
};

const jane: User = { name: "Jane Doe", age: 32 };

An interface uses the interface keyword and no =:

interface User {
  name: string;
  age: number;
}

const john: User = { name: "John Doe", age: 41 };

For plain object shapes like these, the two are almost interchangeable. The choice between them usually comes down to team preference and a few advanced behaviours.

Aspect type alias interface
Describes object shapes Yes Yes
Can name unions, primitives, tuples Yes No
Can be extended / merged later Through intersections Through extends and declaration merging
Common habit Great for everything Often preferred for public object shapes

A simple rule of thumb

If you only need to describe the shape of an object, either one works. Many teams reach for interface for object shapes and use type when they need unions or other non-object types. Pick one convention and stay consistent.

Nesting objects inside objects

Real data is rarely flat. A user might carry an address, and that address is itself an object with its own properties. You describe this by nesting one object type inside another:

interface Address {
  street: string;
  city: string;
  postalCode: string;
}

interface User {
  name: string;
  email: string;
  address: Address;
}

const customer: User = {
  name: "Jane Doe",
  email: "jane@example.com",
  address: {
    street: "Jl. Sudirman 10",
    city: "Jakarta",
    postalCode: "10220",
  },
};

Notice how Address is defined once and then used as the type of the address property. You could also write the nested shape inline, but giving it a name keeps things readable and lets you reuse Address elsewhere.

User
├── name:    string
├── email:   string
└── address: Address
              ├── street:     string
              ├── city:       string
              └── postalCode: string

When you access a nested value, TypeScript follows the structure with you. Type customer.address. in your editor and it will offer street, city, and postalCode — never anything that doesn’t exist on Address.

Optional properties

Not every property is always present. Maybe a user might have a phone number, but it isn’t required. You mark a property as optional by adding a ? right after its name:

interface User {
  name: string;
  email: string;
  phone?: string; // optional
}

const a: User = { name: "Jane Doe", email: "jane@example.com" };          // valid
const b: User = { name: "John Doe", email: "john@example.com", phone: "0812..." }; // also valid

The ? does two things. It allows the property to be missing entirely, and it tells TypeScript that when the property is read, its value might be undefined. So before you use user.phone, you should check that it exists:

function showPhone(user: User) {
  if (user.phone) {
    console.log(user.phone.trim());
  } else {
    console.log("No phone on file");
  }
}

Optional is not the same as nullable

phone?: string means the property may be absent or undefined. It does not mean it can be null. If your data genuinely uses null, type it explicitly, for example phone: string | null. Mixing these up causes confusing errors at runtime.

Readonly properties

Some values should never change after an object is created — an id, for instance. You can protect a property from being reassigned by prefixing it with readonly:

interface User {
  readonly id: number;
  name: string;
}

const user: User = { id: 1, name: "Jane Doe" };

user.name = "Jane Smith"; // fine
user.id = 2;              // Error: Cannot assign to 'id' because it is read-only

readonly is a compile-time guard. It stops you from accidentally writing over the property while you code. It does not freeze the object at runtime — JavaScript itself is unaware of the rule — but in practice it catches a whole class of bugs where a value you meant to keep stable gets quietly overwritten.

Index signatures: typing objects with dynamic keys

Sometimes you don’t know the property names ahead of time. Imagine a scoreboard that maps a player’s name to their score. The names are decided at runtime, so you can’t list them in the type. For this, TypeScript offers the index signature:

interface Scores {
  [playerName: string]: number;
}

const scores: Scores = {
  "Jane Doe": 90,
  "John Doe": 85,
};

scores["Maria Lopez"] = 78; // adding a new key is fine

Read [playerName: string]: number as: “any property whose key is a string holds a number value.” The word playerName is just a label to make the intent clear — you can name it anything. The key type is usually string (or number), and the value type is whatever your data holds.

You can combine an index signature with known, fixed properties, as long as those fixed properties are compatible with the value type of the index:

interface ApiResponse {
  status: number;             // known property
  [header: string]: number;   // plus any number of string-keyed numbers
}

Index signatures trade safety for flexibility

An index signature tells TypeScript that any string key is allowed, so it can no longer warn you about typos in key names. Reach for it only when keys are truly dynamic. When you know the keys, list them out — you’ll get far better checking.

How object types compose

The real power shows up when you start combining shapes instead of rewriting them. TypeScript lets you build bigger types out of smaller ones.

With interfaces, you use extends to inherit properties:

interface Person {
  name: string;
  email: string;
}

interface Employee extends Person {
  employeeId: number;
  department: string;
}

const dev: Employee = {
  name: "John Doe",
  email: "john@acy-partner.com",
  employeeId: 7,
  department: "Engineering",
};

Employee now has all four properties: the two it inherited from Person plus its own two.

With type aliases, you achieve the same result using an intersection, written with &:

type Person = {
  name: string;
  email: string;
};

type Employee = Person & {
  employeeId: number;
  department: string;
};

Both approaches let you describe a small, focused shape once and assemble larger shapes from it. This keeps your types tidy: when the Person fields change, every shape built on top of it updates automatically.

A worked example tying it together

Here is a single example that uses every idea from this guide — naming, nesting, optional, readonly, and an index signature:

interface Address {
  street: string;
  city: string;
}

interface UserProfile {
  readonly id: number;          // never reassigned
  name: string;
  email: string;
  address: Address;             // nested object
  nickname?: string;            // optional
  preferences: {                // inline nested shape
    [setting: string]: boolean; // dynamic keys
  };
}

const profile: UserProfile = {
  id: 1001,
  name: "Jane Doe",
  email: "jane@example.com",
  address: { street: "Jl. Thamrin 1", city: "Jakarta" },
  preferences: {
    darkMode: true,
    emailNotifications: false,
  },
};

Read that type top to bottom and you can predict exactly what a valid profile looks like — which is precisely the point. The type doubles as living documentation.

Recap

Object types are how you describe the shape of structured data so TypeScript can guard it for you. Here is what to carry forward:

  • Inline types ({ name: string }) are great for quick, one-off shapes.
  • type aliases and interfaces give a shape a reusable name; for plain objects they are nearly interchangeable.
  • Nested object types mirror nested data; naming the inner shapes keeps everything readable.
  • Optional properties use ? and may be undefined, so check before you use them.
  • Readonly properties prevent reassignment at compile time, protecting values like IDs.
  • Index signatures ({ [key: string]: number }) describe objects whose keys are dynamic, at the cost of typo protection.
  • Composition with extends (interfaces) or & (type aliases) lets you build larger shapes from smaller ones.

Start by typing the objects you already pass around in your code. Once the editor begins catching missing and misspelled properties for you, you’ll wonder how you ever worked without it.

Tags:typescriptobject typesinterfacetype aliasfrontend
728 × 90Ad Space AvailablePlace your ad here

Related Articles

See All Articles

You Might Also Like

Frontend best practices overview cover
Frontend / Fundamentals

Frontend Best Practices: An Overview

A friendly map of what good frontend really means — semantic HTML, clean CSS, unobtrusive JavaScript, responsive layouts, performance, and progressive enhancement, with pointers to go deeper.

Sep 20, 20268 min read
A Frontend Developer Roadmap cover with the path HTML to CSS to JavaScript
Frontend / Fundamentals

A Frontend Developer Roadmap: What to Learn, and in What Order

A calm, step-by-step learning path for beginners: HTML, CSS, JavaScript, developer tools, responsive and accessible design, a framework, TypeScript, and deployment, with the reasoning behind each step.

Sep 20, 202611 min read
Title card reading Styling and Interactivity over a dark blue ACY Partner background
Frontend / Fundamentals

How Styling and Interactivity Work

A beginner-friendly look at how CSS turns plain HTML into a designed layout, and how JavaScript adds behavior — the structure, style, behavior mental model for building web pages.

Sep 20, 20269 min read
Three front-end layers combining on one web page
Frontend / Fundamentals

How HTML, CSS, and JavaScript Work Together

A hands-on walkthrough for beginners: build one small button, give it structure with HTML, looks with CSS, and behavior with JavaScript, and watch the three layers cooperate on a real page.

Sep 20, 20268 min read
The Frontend Developer Toolkit cover with editor, browser, and CLI motifs
Frontend / Fundamentals

The Frontend Developer Toolkit

A friendly tour of the everyday tools a frontend developer relies on — code editor, browser and DevTools, terminal, version control, package managers, and a dev server — and what each one is actually for.

Sep 20, 202610 min read
Cover illustration for What Frontend Developers Do
Frontend / Fundamentals

What Frontend Developers Do: A Beginner's Guide to the Role

A clear, beginner-friendly look at what frontend developers actually do every day: turning designs into working interfaces, building reusable UI, and caring about responsiveness, accessibility, and speed.

Sep 20, 202610 min read