Interfaces in TypeScript

A Guide to TypeScript Interfaces

Interfaces in TypeScript

Welcome back to our TypeScript series! Today, we’ll explore interfaces, one of the key features that make TypeScript powerful and flexible. Interfaces allow you to define the shape of an object, ensuring that your code adheres to specific contracts and is more predictable and maintainable.

1. Defining Interfaces

An interface defines the structure that an object should adhere to. It can include properties and their types, as well as optional and readonly properties.

Basic Interface

interface Person {
  name: string;
  age: number;
}

let alice: Person = {
  name: "Alice",
  age: 25
};

2. Optional Properties

Optional properties are defined with a question mark (?). They are not required to be present in objects adhering to the interface.

interface Person {
  name: string;
  age?: number;
}

let bob: Person = {
  name: "Bob"
};

3. Readonly Properties

Readonly properties are defined with the readonly keyword. They can only be set once, either at initialization or within the constructor.

interface Person {
  readonly id: number;
  name: string;
}

let charlie: Person = {
  id: 1,
  name: "Charlie"
};

// charlie.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.

4. Function Types

Interfaces can define the shape of functions. This includes specifying the parameter types and return type.

interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean {
  return source.includes(subString);
};

5. Indexable Types

Indexable types allow you to define types for array or object indices. This is useful for defining objects that act like dictionaries or arrays.

interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Alice", "Bob"];

let myStr: string = myArray[0]; // "Alice"

6. Extending Interfaces

Interfaces can extend other interfaces, allowing you to reuse properties and create complex types.

interface Person {
  name: string;
  age: number;
}

interface Employee extends Person {
  employeeId: number;
}

let dave: Employee = {
  name: "Dave",
  age: 30,
  employeeId: 1234
};

7. Hybrid Types

Some objects can act as both functions and objects. TypeScript interfaces allow you to define such hybrid types.

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}

function getCounter(): Counter {
  let counter = function(start: number) { return "Counter: " + start; } as Counter;
  counter.interval = 123;
  counter.reset = function() { console.log("Reset"); };
  return counter;
}

let c = getCounter();
c(10); // "Counter: 10"
c.reset(); // "Reset"
c.interval = 5.0;

8. Interfaces vs. Type Aliases

Both interfaces and type aliases can be used to define the shape of objects, but there are subtle differences and use cases for each.

Interfaces

  • Extendable: Can be extended with other interfaces.

  • Mergeable: Multiple declarations with the same name are merged.

interface Point {
  x: number;
  y: number;
}

interface Point {
  z: number;
}

let point: Point = { x: 1, y: 2, z: 3 };

Type Aliases

  • Flexible: Can represent any type, including primitives, unions, and intersections.

  • Non-mergeable: Cannot be merged with other type aliases of the same name.

type Point = {
  x: number;
  y: number;
};

type ExtendedPoint = Point & { z: number };

let point: ExtendedPoint = { x: 1, y: 2, z: 3 };

9. Declaring Complex Types

You can use interfaces to declare complex types that include nested objects, arrays, and more.

interface Address {
  street: string;
  city: string;
  zipCode: string;
}

interface User {
  name: string;
  age: number;
  address: Address;
  getFullAddress(): string;
}

let user: User = {
  name: "Alice",
  age: 25,
  address: {
    street: "123 Main St",
    city: "Wonderland",
    zipCode: "12345"
  },
  getFullAddress: function () {
    return `${this.address.street}, ${this.address.city}, ${this.address.zipCode}`;
  }
};

console.log(user.getFullAddress()); // "123 Main St, Wonderland, 12345"

Summary

Today, we explored interfaces in TypeScript, covering their definition, optional and readonly properties, function types, indexable types, extending interfaces, hybrid types, and the differences between interfaces and type aliases. Understanding interfaces is crucial for creating robust and maintainable TypeScript applications.

Next time, we’ll dive into classes in TypeScript. Stay tuned!