interface, type, and abstract class can all describe structure, but they are not interchangeable in every situation.
interface
interface is commonly used to describe object shapes, especially public APIs and class contracts.
interface User {
id: string;
name: string;
}
class Admin implements User {
constructor(
public id: string,
public name: string,
) {}
}Interfaces can be merged through repeated declarations, which can be useful when extending third-party types.
type
type is more flexible for unions, intersections, and complex combinations.
type Status = "idle" | "loading" | "success" | "error";
type UserWithRole = User & { role: string };Use type for state unions, function types, and composed type logic.
abstract class
An abstract class can define both a type contract and shared implementation.
abstract class Repository<T> {
abstract findById(id: string): Promise<T | null>;
logQuery(id: string) {
console.log(`find ${id}`);
}
}Use abstract classes when subclasses should share actual code.
Generics
Generics parameterize types.
function identity<T>(value: T): T {
return value;
}
const name = identity<string>("Alice");They are common in collections, API responses, and utility functions.
interface ApiResponse<T> {
data: T;
error?: string;
}
type UserResponse = ApiResponse<User>;Simple Selection Rules
- Object shape: prefer
interface - Union, intersection, utility type: prefer
type - Shared implementation with subclass requirements: use
abstract class - Reusable type logic: use generics