Interfaces and type aliases are core TypeScript features for defining types. This article covers their differences, use cases, and best practices.
Introduction
Interfaces and type aliases provide:
- Type definition
- Code documentation
- Structural typing
- Reusability
- Maintainability
Understanding these helps you:
- Define custom types
- Create contracts
- Extend types
- Compose types
- Write maintainable code
Type Aliases
Basic Type Aliases
// ✅ Good: Basic type aliases
type Name = string;
type Age = number;
type IsActive = boolean;
const name: Name = 'John';
const age: Age = 30;
const active: IsActive = true;
// ✅ Good: Union type aliases
type Status = 'active' | 'inactive' | 'pending';
type ID = string | number;
const status: Status = 'active';
const userId: ID = 123;
// ✅ Good: Object type aliases
type User = {
name: string;
age: number;
email: string;
};
const user: User = {
name: 'John',
age: 30,
email: '[email protected]'
};
// ✅ Good: Function type aliases
type Callback = (value: string) => void;
type Transformer = (value: string) => number;
const callback: Callback = (value) => console.log(value);
const transformer: Transformer = (value) => value.length;
Type Alias Features
// ✅ Good: Intersection types
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;
const person: Person = {
name: 'John',
age: 30
};
// ✅ Good: Conditional types
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<42>; // false
// ✅ Good: Mapped types
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
type ReadonlyUser = Readonly<User>;
Interfaces
Basic Interfaces
// ✅ Good: Basic interface
interface User {
name: string;
age: number;
email: string;
}
const user: User = {
name: 'John',
age: 30,
email: '[email protected]'
};
// ✅ Good: Optional properties
interface Config {
host: string;
port?: number;
timeout?: number;
}
const config: Config = {
host: 'localhost'
};
// ✅ Good: Readonly properties
interface Point {
readonly x: number;
readonly y: number;
}
const point: Point = { x: 10, y: 20 };
// point.x = 30; // Error
// ✅ Good: Method signatures
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
const calc: Calculator = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
Interface Extension
// ✅ Good: Extend interfaces
interface Named {
name: string;
}
interface Aged {
age: number;
}
interface Person extends Named, Aged {
email: string;
}
const person: Person = {
name: 'John',
age: 30,
email: '[email protected]'
};
// ✅ Good: Override properties
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
age: number; // Can override
}
// ✅ Good: Merge interfaces
interface User {
name: string;
}
interface User {
age: number;
}
// Merged interface
const user: User = {
name: 'John',
age: 30
};
Interfaces vs Type Aliases
Comparison
// ✅ Good: Understanding differences
// Type aliases can be unions
type Status = 'active' | 'inactive';
// interface Status = 'active' | 'inactive'; // Error
// Interfaces can be merged
interface User {
name: string;
}
interface User {
age: number;
}
// Type aliases cannot be merged
// Both can extend
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
type Cat = Animal & {
color: string;
};
// Both can be used for objects
interface UserInterface {
name: string;
}
type UserType = {
name: string;
};
// Both work the same for objects
const user1: UserInterface = { name: 'John' };
const user2: UserType = { name: 'Jane' };
When to Use Each
// ✅ Good: Use type aliases for unions
type Status = 'active' | 'inactive' | 'pending';
// ✅ Good: Use interfaces for object contracts
interface User {
name: string;
age: number;
}
// ✅ Good: Use interfaces for class implementation
interface Animal {
name: string;
makeSound(): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log('Woof!');
}
}
// ✅ Good: Use type aliases for complex types
type Result<T> = { success: true; data: T } | { success: false; error: string };
Practical Examples
API Response Types
// ✅ Good: Type-safe API responses
interface ApiResponse<T> {
status: 'success' | 'error';
data?: T;
error?: string;
timestamp: number;
}
interface User {
id: number;
name: string;
email: string;
}
interface Post {
id: number;
title: string;
content: string;
authorId: number;
}
type UserResponse = ApiResponse<User>;
type PostResponse = ApiResponse<Post>;
type UsersResponse = ApiResponse<User[]>;
async function fetchUser(id: number): Promise<UserResponse> {
// Implementation
return {
status: 'success',
data: { id: 1, name: 'John', email: '[email protected]' },
timestamp: Date.now()
};
}
Service Interfaces
// ✅ Good: Service interfaces
interface IUserService {
getUser(id: number): Promise<User>;
createUser(data: Omit<User, 'id'>): Promise<User>;
updateUser(id: number, data: Partial<User>): Promise<User>;
deleteUser(id: number): Promise<void>;
}
interface IAuthService {
login(username: string, password: string): Promise<string>;
logout(): Promise<void>;
verify(token: string): Promise<boolean>;
}
class UserService implements IUserService {
async getUser(id: number): Promise<User> {
// Implementation
return { id, name: 'John', email: '[email protected]' };
}
async createUser(data: Omit<User, 'id'>): Promise<User> {
// Implementation
return { id: 1, ...data };
}
async updateUser(id: number, data: Partial<User>): Promise<User> {
// Implementation
return { id, name: 'John', email: '[email protected]', ...data };
}
async deleteUser(id: number): Promise<void> {
// Implementation
}
}
Generic Interfaces
// ✅ Good: Generic interfaces
interface Repository<T> {
getAll(): Promise<T[]>;
getById(id: number): Promise<T | null>;
create(data: Omit<T, 'id'>): Promise<T>;
update(id: number, data: Partial<T>): Promise<T>;
delete(id: number): Promise<void>;
}
interface Entity {
id: number;
}
interface User extends Entity {
name: string;
email: string;
}
interface Post extends Entity {
title: string;
content: string;
}
class UserRepository implements Repository<User> {
async getAll(): Promise<User[]> {
// Implementation
return [];
}
async getById(id: number): Promise<User | null> {
// Implementation
return null;
}
async create(data: Omit<User, 'id'>): Promise<User> {
// Implementation
return { id: 1, ...data };
}
async update(id: number, data: Partial<User>): Promise<User> {
// Implementation
return { id, name: 'John', email: '[email protected]', ...data };
}
async delete(id: number): Promise<void> {
// Implementation
}
}
Best Practices
- Use interfaces for object contracts:
// ✅ Good interface User { name: string; age: number; } // ❌ Bad type User = { name: string; age: number; }; ```javascript - Use type aliases for unions:
// ✅ Good type Status = 'active' | 'inactive'; // ❌ Bad interface Status { value: 'active' | 'inactive'; } ```javascript - Use interfaces for class implementation:
// ✅ Good interface Animal { name: string; } class Dog implements Animal { name: string; } // ❌ Bad type Animal = { name: string }; class Dog implements Animal { } // Error ```javascript
Common Mistakes
- Using type aliases for everything:
// ❌ Bad type User = { name: string }; type Config = { host: string }; // ✅ Good interface User { name: string } interface Config { host: string } ```javascript - Not extending interfaces:
// ❌ Bad interface User { name: string; age: number; email: string; } interface Admin { name: string; age: number; email: string; role: string; } // ✅ Good interface User { name: string; age: number; email: string; } interface Admin extends User { role: string; }
Summary
Interfaces and type aliases are essential. Key takeaways:
- Use type aliases for unions
- Use interfaces for object contracts
- Extend interfaces for reuse
- Use generics for flexibility
- Implement interfaces in classes
- Merge interfaces when needed
- Choose appropriate tool for task
- Document with types
Related Resources
Next Steps
- Learn about Generics
- Explore Decorators and Metadata
- Study Advanced Type Patterns
- Practice interface design
- Build type-safe applications
Comments