LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 515 min read

Interfaces

Learn how to describe object shapes with interfaces, including optional and readonly properties, extending interfaces, and structural typing.

Introduction

Most real-world data isn't a single primitive value — it's an object with several properties: a user with an id, name, and email; a product with a price and description. Interfaces are TypeScript's primary tool for describing the shape of those objects.

What You Will Learn
  • How to define an interface and use it to type an object.
  • How to mark properties as optional or readonly.
  • How to extend one interface with another to share common fields.
  • How to describe function types using an interface.
  • What structural typing means and why it matters in TypeScript.

What is an Interface?

An interface is a named description of an object's shape — which properties it must have, and what type each property is. Any object that has all of the required properties, with matching types, satisfies the interface.

interface User {
id: number;
name: string;
email: string;
}
function printUser(user: User): void {
console.log(`${user.name} (${user.email})`);
}
printUser({ id: 1, name: "Amol", email: "amol@example.com" });
Output

Click Run to see what this code prints.

Optional and Readonly Properties

Adding a `?` after a property name makes it optional — objects satisfying the interface may omit it entirely. Adding `readonly` before a property means it can be set once (usually at creation) but never reassigned afterward.

interface Product {
readonly id: number;
name: string;
discount?: number; // optional
}
const item: Product = { id: 101, name: "Keyboard" };
// item.id = 202; // Error: 'id' is readonly

Extending Interfaces

One interface can build on another with `extends`, inheriting all of its properties. This avoids repeating shared fields across related shapes.

interface Employee extends User {
department: string;
}
const worker: Employee = {
id: 2,
name: "Priya",
email: "priya@example.com",
department: "Engineering",
};

Interfaces for Functions

Interfaces can also describe the shape of a function — its parameters and return type — which is useful when you want to name a reusable "callback shape."

interface Comparator {
(a: number, b: number): number;
}
const ascending: Comparator = (a, b) => a - b;

Structural Typing

TypeScript uses structural typing, sometimes called "duck typing": what matters is whether an object's *shape* matches an interface, not whether it was explicitly declared to implement it. This mirrors how JavaScript already works at runtime — objects don't carry a formal "type name" with them, only their actual properties.

Implementing Interfaces in Classes

A class can declare that it satisfies an interface using `implements`, which the compiler checks and enforces. You'll see this in depth in the classes lesson, but the shape is: `class Manager implements Employee { ... }` — TypeScript then requires every property and method the interface declares.

Common Mistakes

Avoid These Mistakes
  • Being surprised by "excess property check" errors when passing an object literal with extra fields directly into a function expecting an interface.
  • Assuming `readonly` makes nested objects fully immutable — it is shallow; a readonly property holding an object can still have its own inner properties changed.
  • Forgetting that declaring the same interface name twice is actually valid TypeScript — interfaces merge declarations, unlike type aliases.
  • Prefixing interface names with "I" (e.g. `IUser`) out of habit from other languages — that convention is not idiomatic in TypeScript/JavaScript.

Best Practices

  • Name interfaces after what they represent (`User`, `ApiResponse`), without an "I" prefix.
  • Mark properties `readonly` whenever they are set once and never intended to change.
  • Prefer extending a shared base interface over duplicating common fields across related shapes.
  • Use interfaces to describe the public shape of objects and classes, especially in code that other modules will consume.

Frequently Asked Questions

TypeScript compares shapes, not names — any object with the right properties satisfies an interface, even if it was never explicitly declared to implement it.

Yes — `interface C extends A, B {}` is valid and combines the properties of both.

`age?: number` allows the key to be entirely absent from the object. `age: number | undefined` requires the key to exist, but permits its value to be undefined.

No — like all TypeScript types, interfaces are fully erased during compilation and have no representation in the compiled JavaScript.

Key Takeaways

  • Interfaces describe the shape of an object: its properties and their types.
  • `?` marks a property optional; `readonly` prevents reassignment after creation.
  • Interfaces can extend other interfaces to share common fields.
  • TypeScript uses structural typing — matching shape is what satisfies an interface, not an explicit declaration.
  • Classes can use `implements` to guarantee they satisfy a given interface.

Summary

You can now describe the shape of objects with interfaces, including optional and readonly properties and extension. Next, you'll learn type aliases and union types — TypeScript's tools for describing values that can be one of several types, and how they compare to interfaces.

Next Lesson →

Type Aliases & Union Types