Type Aliases
Type aliases create a new name for a type:
// Simple type alias
type ID = string | number;
type Username = string;
// Object type alias
type User = {
id: ID;
name: string;
email: string;
age?: number; // optional
};
// Function type alias
type Greeting = (name: string) => string;
// Union type alias
type Status = "pending" | "active" | "completed";
// Using type aliases
let userId: ID = "abc123";
let user: User = {
id: 1,
name: "Alice",
email: "alice@example.com"
};
const greet: Greeting = (name) => `Hello, ${name}!`;
Interfaces
Interfaces define the shape of objects:
// Basic interface
interface User {
id: number;
name: string;
email: string;
age?: number; // optional property
readonly createdAt: Date; // readonly property
}
// Using the interface
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com",
createdAt: new Date()
};
// Function interface
interface SearchFunc {
(query: string, limit?: number): string[];
}
const search: SearchFunc = (query, limit = 10) => {
return ["result1", "result2"];
};
// Index signature
interface StringDictionary {
[key: string]: string;
}
const dict: StringDictionary = {
hello: "world",
foo: "bar"
};
Extending Interfaces
// Base interface
interface Animal {
name: string;
age: number;
}
// Extending a single interface
interface Dog extends Animal {
breed: string;
bark(): void;
}
const dog: Dog = {
name: "Max",
age: 3,
breed: "Labrador",
bark() {
console.log("Woof!");
}
};
// Extending multiple interfaces
interface Timestamps {
createdAt: Date;
updatedAt: Date;
}
interface Post extends Animal, Timestamps {
title: string;
content: string;
}
// Extending with override (must be compatible)
interface SpecialAnimal extends Animal {
age: 1 | 2 | 3; // narrower type is OK
}
Intersection Types
// Type aliases can use intersections
type Animal = {
name: string;
age: number;
};
type CanFly = {
fly(): void;
wingspan: number;
};
// Combine types with &
type Bird = Animal & CanFly;
const eagle: Bird = {
name: "Eagle",
age: 5,
wingspan: 2.3,
fly() {
console.log("Flying!");
}
};
// Intersection with interfaces too
interface Person {
name: string;
}
interface Employee {
employeeId: number;
department: string;
}
type Staff = Person & Employee;
const staff: Staff = {
name: "Alice",
employeeId: 123,
department: "Engineering"
};
Interface Declaration Merging
Interfaces can be declared multiple times and merge:
// First declaration
interface Config {
apiUrl: string;
}
// Second declaration (merges with first)
interface Config {
timeout: number;
}
// Result: Config has both properties
const config: Config = {
apiUrl: "https://api.example.com",
timeout: 5000
};
// Useful for extending library types
interface Window {
myCustomProperty: string;
}
// Now window.myCustomProperty is typed
Type Aliases vs Interfaces
// Both can define object shapes
type UserType = {
name: string;
age: number;
};
interface UserInterface {
name: string;
age: number;
}
// Type aliases can define primitives, unions, tuples
type ID = string | number; // Can't do with interface
type Coordinates = [number, number]; // Can't do with interface
type Status = "active" | "inactive"; // Can't do with interface
// Interfaces can be extended and merged
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// Types use intersection for composition
type Animal2 = { name: string };
type Dog2 = Animal2 & { breed: string };
// Interfaces support declaration merging
interface User { name: string; }
interface User { age: number; } // Merges
// Type aliases can't merge
// type User = { name: string };
// type User = { age: number }; // Error: Duplicate identifier
When to Use What
Use Interfaces When:
- • Defining object shapes
- • Creating contracts for classes
- • You need declaration merging
- • Working with libraries/APIs
Use Type Aliases When:
- • Defining union types
- • Defining tuple types
- • Creating type utilities
- • Aliasing primitives
Practical Examples
// API Response typing
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
interface User {
id: number;
name: string;
email: string;
}
type UserResponse = ApiResponse<User>;
type UsersResponse = ApiResponse<User[]>;
// Component props
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "danger";
disabled?: boolean;
}
// Form data
interface LoginForm {
username: string;
password: string;
rememberMe?: boolean;
}
// Event handlers
type ClickHandler = (event: MouseEvent) => void;
type ChangeHandler = (value: string) => void;
Key Takeaways
- • Type aliases create new names for types
- • Interfaces define contracts for object shapes
- • Interfaces can extend other interfaces
- • Type aliases use
&for intersections - • Interfaces support declaration merging
- • Use interfaces for objects, types for unions/primitives