LearnAI ToolsCareerPractice BuildsPlayDevelopmentContact
Lesson 515 min read

Interfaces

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

On this page 12 sections

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