Learn how to define and use interfaces in TypeScript to describe object shapes and enforce contracts within your code.
In the world of TypeScript, interfaces play a crucial role in defining the structure of objects and ensuring that our code adheres to specific contracts. As we delve into this topic, we’ll explore what interfaces are, how they work, and why they are an essential tool for TypeScript developers.
Interfaces in TypeScript are a way to define the shape of an object. They act as a blueprint for objects, specifying what properties and methods an object should have. Interfaces help enforce a contract within your code, ensuring that objects adhere to a specific structure.
Think of an interface as a contract that an object must fulfill. If an object claims to implement an interface, it must have all the properties and methods defined by that interface.
Interfaces provide several benefits:
Let’s start by declaring a simple interface in TypeScript. We’ll define an interface for a Person
object:
interface Person {
name: string;
age: number;
greet(): void;
}
In this example, the Person
interface specifies that any object adhering to it must have a name
property of type string
, an age
property of type number
, and a greet
method that returns void
.
Once an interface is defined, we can use it to type-check objects:
const person: Person = {
name: "Alice",
age: 30,
greet() {
console.log(`Hello, my name is ${this.name}`);
}
};
person.greet(); // Output: Hello, my name is Alice
In this example, the person
object adheres to the Person
interface, ensuring that it has the required properties and methods.
Interfaces enforce the presence and types of properties. If we try to create an object that doesn’t match the interface, TypeScript will throw an error:
const invalidPerson: Person = {
name: "Bob",
// Missing 'age' property
greet() {
console.log(`Hello, my name is ${this.name}`);
}
};
// Error: Property 'age' is missing in type '{ name: string; greet(): void; }' but required in type 'Person'.
TypeScript uses a concept known as “duck typing” or “structural subtyping.” This means that TypeScript checks the structure of an object rather than its explicit type. If an object has all the properties and methods required by an interface, it is considered to implement that interface, regardless of its actual type.
For example:
interface Point {
x: number;
y: number;
}
function logPoint(point: Point) {
console.log(`x: ${point.x}, y: ${point.y}`);
}
const point = { x: 10, y: 20, z: 30 };
logPoint(point); // Output: x: 10, y: 20
In this example, point
has an extra property z
, but it still satisfies the Point
interface because it has the required x
and y
properties.
Interfaces can be extended to create new interfaces with additional properties. This is useful for building upon existing interfaces:
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
}
const myDog: Dog = {
name: "Buddy",
age: 5,
breed: "Golden Retriever"
};
Here, the Dog
interface extends the Animal
interface, adding a breed
property.
Let’s put what we’ve learned into practice. Try defining interfaces for the following objects:
make
, model
, and year
property, as well as a startEngine
method.title
, author
, and pages
property, as well as a read
method.Exercise 1: Car Interface
interface Car {
make: string;
model: string;
year: number;
startEngine(): void;
}
const myCar: Car = {
make: "Toyota",
model: "Corolla",
year: 2020,
startEngine() {
console.log("Engine started!");
}
};
myCar.startEngine(); // Output: Engine started!
Exercise 2: Book Interface
interface Book {
title: string;
author: string;
pages: number;
read(): void;
}
const myBook: Book = {
title: "TypeScript for Beginners",
author: "John Doe",
pages: 300,
read() {
console.log(`Reading ${this.title} by ${this.author}`);
}
};
myBook.read(); // Output: Reading TypeScript for Beginners by John Doe
Now that you’ve seen how interfaces work, try modifying the code examples above. Add new properties or methods to the interfaces, or create your own interfaces for different objects. Experimentation is a great way to solidify your understanding of interfaces.
To better understand how interfaces define the structure of objects, let’s visualize the relationship between interfaces and objects using a diagram.
classDiagram class Person { +string name +int age +void greet() } class Employee { +string name +int age +string position +void greet() +void work() } Person <|-- Employee
In this diagram, the Employee
class extends the Person
interface, inheriting its properties and methods while adding its own.
For more information on interfaces in TypeScript, check out the following resources: