Interfaces & Type Aliases

TypeScript Fundamentals
Course 1 Β· Chapter 4 Β· Interfaces & Type Aliases

🀝 Interfaces & Type Aliases

So far, you've written type annotations inline. As your code grows, repeating the same object shape becomes tedious and error-prone. This chapter introduces two ways to name and reuse types: type aliases and interfaces. Both solve the problemβ€”knowing when to use each is the art.

🏷️ Type Aliases

A type alias gives a name to any type using the type keyword. It's like assigning a type to a variable.

Basic Type Alias

type Age = number;

const myAge: Age = 30;  // βœ… Works
const yourAge: Age = "thirty";  // ❌ Error! Type 'string' is not assignable to type 'number'

Type Alias for Objects

The real power: reuse object shapes across your codebase:

type User = {
  name: string;
  email: string;
  age: number;
  isActive?: boolean;
};

const user1: User = { name: "Alice", email: "alice@example.com", age: 30 };
const user2: User = { name: "Bob", email: "bob@example.com", age: 25, isActive: true };

// βœ… Both match the User type
greet(user1);
greet(user2);

Union Type Aliases

Type aliases shine with unions β€” combining multiple types:

type ID = string | number;
type Status = "pending" | "active" | "inactive";

const userId: ID = 42;  // βœ… number is OK
const username: ID = "user123";  // βœ… string is OK

const currentStatus: Status = "active";  // βœ… Valid status
const badStatus: Status = "deleted";  // ❌ Not a valid status option

πŸ“‹ Interfaces

An interface is specifically for describing the shape of objects. It's similar to a type alias for objects, but with some key differences (which we'll explore).

Basic Interface

interface User {
  name: string;
  email: string;
  age: number;
  isActive?: boolean;
}

const user: User = { name: "Alice", email: "alice@example.com", age: 30 };

Notice the syntax difference: no `=` sign, no curly braces required.

Extending Interfaces

Interfaces can inherit from other interfaces using extends:

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

interface AdminUser extends User {
  role: "admin";
  permissions: string[];
}

// AdminUser now has name, email, role, and permissions
const admin: AdminUser = {
  name: "Alice",
  email: "alice@example.com",
  role: "admin",
  permissions: ["read", "write", "delete"]
};

Merging Interfaces

A unique power of interfaces: you can declare the same interface multiple times and they merge:

interface User {
  name: string;
}

interface User {
  age: number;
}

// User now has both name AND age
const user: User = { name: "Alice", age: 30 };

This is powerful for adding properties to types across different files (think plugins).

βš–οΈ Type Aliases vs Interfaces

When to Use Each

Use Interfaces For:
  • Object shapes (contracts)
  • Classes to implement
  • Code that merges types
  • Public API definitions
Use Type Aliases For:
  • Unions (string | number)
  • Tuples ([string, number])
  • Primitives (numbers, strings)
  • Complex compositions

Key Differences

βœ… Interfaces can extend
interface Admin extends User { ... }
βœ… Interfaces merge
interface Window { x: number; }
interface Window { y: number; }
βœ… Types support unions
type Status = "on" | "off";
βœ… Types are more flexible
type Callback = () => void;

πŸ”§ Methods in Interfaces

Interfaces can describe methods (functions) as properties:

interface Logger {
  log(message: string): void;
  error(message: string): void;
}

const consoleLogger: Logger = {
  log: (msg) => console.log(msg),
  error: (msg) => console.error(msg)
};

πŸ”’ Readonly and Intersection Types

Readonly Properties

interface Config {
  readonly apiKey: string;
  port: number;
}

const config: Config = { apiKey: "secret", port: 3000 };
config.port = 4000;  // βœ… Fine
config.apiKey = "newsecret";  // ❌ Error! Cannot assign to readonly

Intersection Types (Preview)

Combine multiple types into one using &:

type Employee = User & { employeeId: number };

// Employee has all properties from User PLUS employeeId
const emp: Employee = {
  name: "Alice",
  email: "alice@example.com",
  age: 30,
  employeeId: 12345
};

πŸ’» Coding Challenges

Challenge 1: Create a User Type Alias

Define a type alias for a user with:

  • id (number)
  • username (string)
  • email (string)
  • isVerified (boolean, optional)

Goal: Create the type alias and use it to define 2-3 user objects.

β†’ Solution

Challenge 2: Extend an Interface

Create a base Person interface with name and age, then create an Employee interface that extends it and adds:

  • employeeId (number)
  • department (string)

Goal: Demonstrate interface inheritance.

β†’ Solution

Challenge 3: Union Type for Status

Create a Status type that can only be one of: "pending", "approved", or "rejected". Then create a function that accepts this type and logs the status.

function handleStatus(status: Status): void {
  // Your implementation
}

Goal: Demonstrate union types with literals.

β†’ Solution

πŸ’‘ Real-World Tip: Start with Interfaces

As a beginner, favor interfaces for object shapes and type aliases for everything else. Interfaces are slightly more limited, which forces you to write clearer, more maintainable code. You can always switch to a type alias if you need unions or other advanced features.

🎯 What's Next

Now that you can define contracts with types and interfaces, Chapter 5 introduces Classes & Object-Oriented TypeScript, where you'll see interfaces in action as contracts that classes implement.