Interfaces

In TypeScript, interfaces allow you to define the structure of an object. They serve as a blueprint, ensuring that objects follow a specific shape, which is useful in creating predictable and maintainable code.

Why Use Interfaces?

  • Consistency: Interfaces ensure that objects adhere to a specified structure, which makes your code more predictable.
  • Object-Oriented Programming: Interfaces align well with OOP, as they allow you to define object types clearly.
  • Extensibility: Interfaces can be extended, allowing flexibility in defining related object types.

Defining an Interface

Let's start by creating an interface for a simple object that represents a User:

interface User {
  id: number;
  name: string;
  email: string;
}

const newUser: User = {
  id: 1,
  name: "Alice",
  email: "alice@example.com",
};

Here, User is an interface that requires any object of type User to have an id, name, and email.

Comparing type and interface

Both type and interface can be used to define object types, but they have a few differences. Here's a quick comparison:

  • Interface can only be used for objects, while type can handle primitives, union types, and other complex structures.
  • Extensibility: Interfaces can be extended using the extends keyword.
// Using type
type Address = {
  street: string;
  city: string;
  zipCode: number;
};

// Using interface
interface Address {
  street: string;
  city: string;
  zipCode: number;
}

Extending Interfaces

nterfaces in TypeScript are extendable, which allows you to build on top of existing structures without modifying them directly.

interface User {
  id: number;
  name: string;
}

interface Admin extends User {
  adminLevel: number;
}

const adminUser: Admin = {
  id: 1,
  name: "Bob",
  adminLevel: 5,
};

In this example, Admin extends User, meaning Admin includes all properties of User plus the new adminLevel property.

Optional Properties

Interfaces allow optional properties by adding a question mark (?) to a property’s name. This helps define objects that may not require every property.

interface Product {
  id: number;
  name: string;
  description?: string;
}

const product1: Product = { id: 101, name: "Laptop" };
const product2: Product = { id: 102, name: "Phone", description: "Latest model" };

Function Types in Interfaces

Interfaces can also be used to define function signatures.

interface Greet {
  (name: string): string;
}

const sayHello: Greet = (name) => `Hello, ${name}!`;

console.log(sayHello("Alice")); // Output: Hello, Alice!

Function Types: : vs. =>

When defining function types within interfaces, you'll encounter two ways to specify return types:

  1. Colon (:) syntax, typically used in interfaces:
interface Greet {
  (name: string): string; // Function takes a string and returns a string
}
  1. Arrow (=>) syntax, often seen in arrow functions outside of interfaces but also valid within:
interface Greet {
  (name: string) => string; // Function type using arrow notation
}

Although both are valid, the : syntax is more commonly used in interfaces, while => is frequent in standalone arrow functions. Both achieve the same result—defining the function's input and return type—but having both options can help in different contexts.

Using Interfaces with Classe

Interfaces are powerful when working with classes, as they allow you to define class structures.

interface Animal {
  name: string;
  makeSound(): void;
}

class Dog implements Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Woof!");
  }
}

const myDog = new Dog("Buddy");
myDog.makeSound(); // Output: Woof!

In this example, the Dog class implements the Animal interface, ensuring that it has both a name and a makeSound method.

Summary

Interfaces provide structure and consistency in TypeScript, making them valuable tools in building scalable, maintainable applications. By using interfaces, you can create clear and extendable code while leveraging TypeScript's type-checking capabilities.