Introduction: The Eternal Debate of TypeScript Developers
If you've ever written in TypeScript, you've surely encountered the question: "When should I use interface, and when should I use type?" This question has gathered thousands of views on Stack Overflow and has become a real stumbling block for beginners. Let's finally sort out this dilemma once and for all.
TypeScript is a superset of JavaScript that adds static typing. And we have two main tools for describing data structures: Interface and Type Alias. At first glance, they do the same thing, but the devil is in the details. In this article, we won't just list the differences — we'll show them with real examples and give clear recommendations.
What is an Interface?
An Interface is a way to describe the structure of an object. It's like a blueprint for data: you tell TypeScript what properties and methods an object should have.
// Basic interfaceinterface User { name: string; age: number; email?: string; // optional property readonly id: number; // read-only}
// Usageconst user: User = { name: "Anna", age: 25, id: 1};Interfaces can inherit from each other, making them ideal for describing hierarchies:
interface Animal { name: string; sound(): void;}
interface Dog extends Animal { breed: string; wagTail(): void;}
// Now Dog contains all Animal fields + its ownconst myDog: Dog = { name: "Rex", breed: "Shepherd", sound() { console.log("Woof!"); }, wagTail() { console.log("Wags tail"); }};What is a Type Alias?
A Type Alias is a more flexible tool. It can describe not only objects but also primitives, unions, and intersections of types.
// Type for a primitivetype ID = string | number;
// Type for an objecttype UserType = { name: string; age: number;};
// Type for a functiontype Greeting = (name: string) => string;
// Union Typetype Status = "active" | "inactive" | "pending";
// Intersection Typetype Person = { name: string };type Employee = Person & { salary: number };The main superpower of types is union types. Interfaces can't do that:
// ❌ This is not possible with interface// interface Status = "active" | "inactive"; // Error!
// ✅ But with type it's easy!type Status = "active" | "inactive" | "pending";
function processStatus(status: Status) { switch (status) { case "active": console.log("Active"); break; case "inactive": console.log("Inactive"); break; case "pending": console.log("Pending"); break; }}Key Differences: Interface vs Type
Let's break down how they differ in practice.
1. Union and Intersection Types
Type supports union (|) and intersection (&) directly. Interface only through extends.
// Union — only typetype Success = { data: string };type Error = { error: string };type Result = Success | Error; // Possible!
// Intersectiontype Full = Success & Error; // Possible!
// For interface, you'd have to create separate interfacesinterface IResult extends Success, Error {} // Works, but is cumbersome2. Declaration Merging
This is a unique feature of interfaces. If you declare two interfaces with the same name, TypeScript will merge them.
// First interfaceinterface User { name: string;}
// Second interface with the same nameinterface User { age: number;}
// Result: User = { name: string; age: number; }const user: User = {