Interfaces in TypeScript Explained
TypeScript interfaces are named contracts that describe the shape an object must have. Learn how to define them, how structural typing works, and how they support extension and merging.
TypeScript interfaces are named descriptions of an object's shape. An interface lists the properties an object must have and the types those properties must be. You define an interface once and use it everywhere that shape is expected.
Think of an interface as a contract. Any object that has the properties listed in the interface satisfies the contract. The object can have extra properties too. TypeScript only checks that the required ones are present and have the right types.
Defining an interface
An interface declaration names a shape once so every function and variable can reuse it instead of repeating an inline object type. Use the interface keyword, followed by a name and a block of property declarations.
interface User {
name: string;
age: number;
}
function greet(user: User) {
console.log(`Hello, ${user.name}.`);
}
const ada: User = { name: "Ada", age: 30 };
greet(ada);The User interface says: any User must have a name of type string and an age of type number. The compiler checks the ada variable at the assignment and again at the function call.
Interfaces can include optional properties with a question mark and readonly properties with the readonly keyword. For example, a Config interface might have a required name, an optional timeout, and a readonly id that cannot change after creation.
Interfaces and structural typing
TypeScript uses structural typing, sometimes called duck typing: if it looks like a duck and quacks like a duck, it is a duck. The compiler only checks the shape of the value. It does not care whether the value was explicitly declared as implementing the interface.
interface Labeled {
label: string;
}
function printLabel(obj: Labeled) {
console.log(obj.label);
}
const item = { label: "Milk", price: 2.5, inStock: true };
printLabel(item); // "Milk"The item variable was not declared as Labeled. It was inferred as an object with label, price, and inStock. But it has a label of type string, so it satisfies the Labeled contract. TypeScript allows it.
This is different from languages like Java or C#, where a class must explicitly declare that it implements an interface. In TypeScript, no explicit declaration is needed. The shape alone is enough.
Extending interfaces
Interfaces can extend other interfaces. The extending interface inherits all properties of the base and adds its own.
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const rex: Dog = { name: "Rex", breed: "Golden Retriever" };Dog extends Animal, so it requires both name from Animal and breed from itself. You can extend multiple interfaces at once, combining their contracts into a single interface. The resulting interface requires all properties from all parent interfaces, plus any properties declared in its own body.
Extension is a way to compose interfaces from smaller, focused pieces. Instead of one large interface with every property, break it into reusable contracts and combine them with extends.
Declaration merging
One unique feature of interfaces is declaration merging. If you declare the same interface name twice in the same scope, TypeScript merges the declarations into a single interface with all properties combined.
interface Window {
title: string;
}
interface Window {
version: number;
}
const win: Window = { title: "My App", version: 2 };The merged Window requires both title and version. This is commonly used to augment built-in types or third-party library types without modifying the original source. Type aliases cannot do this -- trying to declare the same type alias twice produces a duplicate identifier error.
Interfaces vs inline object types
You can describe an object's shape inline without naming it. This works for a single function parameter, but repeating the inline type across multiple functions is error-prone.
Interfaces give the shape a name, which makes the code more readable and easier to change. Interfaces also appear by name in error messages. An error that says "missing property Y from type User" is much clearer than one that prints the full anonymous type.
| Inline object type | Interface | |
|---|---|---|
| Reusable across files | No, must be retyped | Yes, imported by name |
| Appears in error messages | As a full anonymous shape | By name |
Extendable with extends | No | Yes |
For more on the differences between interfaces and the type keyword, see interface vs type in TypeScript. Also see type aliases in TypeScript explained for the other side of the naming story.
When to use interfaces
Use an interface when you are describing the shape of an object, when you want the type to be extendable later, when you want the type name to appear in error messages, and when you are building a public API where consumers may need to augment types.
Use a type alias instead when you need to describe a union, intersection, tuple, or mapped type. Interfaces can only describe object shapes.
Common mistakes
Forgetting that extra properties are allowed. An object with more properties than the interface requires is still valid, as long as it is not an object literal passed directly to a typed position. The excess property check only applies to literals.
Expecting an implements relationship between objects and interfaces. TypeScript does not require a declaration. If an object has the right properties with the right types, it satisfies the interface. This is structural typing, and it is by design.
Trying to use extends on a type alias. extends only works on interfaces. To combine type aliases, use intersection types with the ampersand operator.
Rune AI
Key Insights
- An interface defines a named contract that objects satisfy by having the right properties.
- TypeScript uses structural typing: an object satisfies an interface if it has the required properties.
- Extend interfaces with extends to build new contracts from existing ones.
- Interfaces support declaration merging: declaring the same name twice merges the properties.
- Interfaces always appear by name in error messages, which helps readability.
Frequently Asked Questions
Can interfaces describe function types?
Can I add properties to an existing interface?
Do interfaces exist at runtime?
Conclusion
An interface is a named contract for object shapes. It describes what properties an object must have and their types. Interfaces support extension through the extends keyword and can participate in declaration merging. Use interfaces when you want a named, reusable, and extendable description of an object's structure.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.