Interfaces & Types: Modeling Custom Data

Interfaces & Types: Modeling Custom Data

In real-world applications, you rarely work with just strings or numbers. You work with complex objects: Users, Products, API Responses, and more. TypeScript provides two powerful ways to define the "shape" of these objects: Interfaces and Type Aliases.


1. Interfaces: Defining Object Contracts

An interface is a powerful way to define the structure of an object. It acts as a contract that a variable or class must fulfill.

Basic Interface

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

const user: User = { id: 1, username: "dev_pro" };

Advanced Interface Features

  • Optional Properties (?): Properties that don't have to be present.
  • Readonly Properties (readonly): Properties that can only be set when the object is first created.
  • Function Types: Interfaces can also describe how a function should look.
interface SmartDevice {
    readonly serialNumber: string;
    model: string;
    batteryLife?: number; // Optional
    turnOn: (mode: string) => boolean; // Function signature
}

2. Type Aliases: The Flexible Alternative

A type alias allows you to create a new name for any type, including primitives, unions, and intersections.

Union Types (|)

Used when a value can be one of several types.

type ID = string | number;
let userId: ID = 101;
userId = "uuid-abcd";

Intersection Types (&)

Used to combine multiple types into one.

interface HasName { name: string; }
interface HasAge { age: number; }

type Person = HasName & HasAge;

const worker: Person = { name: "John", age: 30 };

3. Extending Types

Both Interfaces and Type Aliases can be extended, but the syntax differs.

Extending an Interface (Inheritance)

interface Animal { name: string; }
interface Dog extends Animal { breed: string; }

Extending via Intersections

type Animal = { name: string; };
type Dog = Animal & { breed: string; };

4. Key Differences: Interface vs. Type

While they are very similar, there are important technical differences:

  1. Declaration Merging: You can define the same interface twice, and TypeScript will merge them into one. You cannot do this with type.
    interface Window { title: string; }
    interface Window { width: number; }
    // Result: Window has both title and width.
    
  2. Primitives/Unions: A type can alias a primitive (type Name = string) or a union. An interface can only describe objects or functions.
  3. Extensibility: Interfaces are generally better for public APIs and libraries because they are designed for inheritance and merging.

5. Index Signatures

Sometimes you don't know the exact property names in advance (e.g., data from a database). You can use an Index Signature to define the types of keys and values.

interface StringDictionary {
    [key: string]: string; // All keys must be strings, all values must be strings
}

const translations: StringDictionary = {
    "hello": "hola",
    "world": "mundo"
};

6. Summary

  • Use interface for object shapes, especially if you need to use extends or Declaration Merging.
  • Use type for complex unions, intersections, or when aliasing primitives.
  • Always favor Consistency within your project.