Basic Interfaces
Interfaces define the shape of objects. They act as contracts that classes and object literals must fulfill.
// Basic interface
interface User {
id: number;
name: string;
email: string;
age?: number; // Optional property
readonly createdAt: Date; // Cannot be changed after creation
}
// Interface with methods
interface Repository<T> {
findById(id: number): Promise<T | null>;
findAll(): Promise<T[]>;
create(data: Omit<T, 'id'>): Promise<T>;
update(id: number, data: Partial<T>): Promise<T>;
delete(id: number): Promise<void>;
}
// Implementing an interface in a class
class UserRepository implements Repository<User> {
private users: User[] = [];
async findById(id: number): Promise<User | null> {
return this.users.find(u => u.id === id) ?? null;
}
async findAll(): Promise<User[]> {
return [...this.users];
}
async create(data: Omit<User, 'id'>): Promise<User> {
const user: User = { ...data, id: Date.now() };
this.users.push(user);
return user;
}
async update(id: number, data: Partial<User>): Promise<User> {
const index = this.users.findIndex(u => u.id === id);
if (index === -1) throw new Error('User not found');
this.users[index] = { ...this.users[index], ...data };
return this.users[index];
}
async delete(id: number): Promise<void> {
this.users = this.users.filter(u => u.id !== id);
}
}
Extending Interfaces
Interfaces can extend other interfaces, enabling compositional type definitions.
interface Entity {
id: string;
createdAt: Date;
updatedAt: Date;
}
interface User extends Entity {
name: string;
email: string;
}
interface AdminUser extends User {
role: 'admin';
permissions: string[];
lastLogin: Date;
}
// Extending multiple interfaces
interface AuditableEntity extends Entity {
createdBy: string;
updatedBy: string;
}
interface AuditableUser extends User, AuditableEntity {
// Has all properties from User and AuditableEntity
}
// Interface declaration merging (unique to interfaces)
interface Config {
theme: 'dark' | 'light';
}
interface Config {
language: string; // Merged with the above Config
}
// Now Config has both theme and language
const config: Config = {
theme: 'dark',
language: 'en',
};
Interface vs Type Alias
Both interface and type can define object shapes. Here are the key differences to help you choose.
// Both can define object shapes
interface UserInterface {
name: string;
age: number;
}
type UserType = {
name: string;
age: number;
};
// Difference 1: Declaration merging (only interfaces)
interface Window {
myCustomProperty: string; // Extends built-in Window type
}
// Difference 2: Computed properties (only types)
type Keys = 'name' | 'email';
type UserFromKeys = {
[K in Keys]: string; // Can't do this with interface
};
// Difference 3: Union/intersection (types are more powerful)
type Result = 'success' | 'error'; // Union - can't do with interface
type Combined = UserInterface & { role: string }; // Intersection
// Recommendation:
// Use 'interface' for object shapes and class contracts
// Use 'type' for unions, intersections, mapped types, and aliases
The TypeScript team recommends using interface for public API definitions and type for complex type transformations.