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.
- 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" });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 readonlyExtending 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
- 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.