Interfaces vs Type Aliases
In the last lesson you described objects with type. TypeScript has a second way to name an object shape: the interface keyword. For simple objects the two are almost the same, which confuses many beginners. Which one should you use, and when does the difference matter?
In this lesson you will write interfaces, extend them, combine types with intersections, see the few real differences between interfaces and type aliases, and learn a simple rule for choosing.
Your first interface
An interface looks like a type alias without the = sign:
interface Teacher {
id: number;
name: string;
subjects: string[];
phone?: string;
greet(): string;
}
const meera: Teacher = {
id: 7,
name: "Meera Iyer",
subjects: ["Maths", "Physics"],
greet() {
return `Good morning, I am ${this.name}`;
},
};
console.log(meera.greet());
console.log(`${meera.name} teaches ${meera.subjects.join(" and ")}`);
console.log(meera.phone ?? "phone not shared");Good morning, I am Meera Iyer Meera Iyer teaches Maths and Physics phone not shared
Everything you learned about object types works the same: optional properties with ?, readonly, nested types, index signatures and structural typing. greet(): string is method syntax: it describes a function property. You could also write it as greet: () => string.
The same shape written as a type alias would be:
type Teacher = {
id: number;
name: string;
subjects: string[];
phone?: string;
greet(): string;
};Extending interfaces and intersecting types
Real data often shares common fields. An interface can extend one or more other interfaces, copying in all their properties. Type aliases do the same job with an intersection, written with &, which means "has everything from both types".
interface Person {
name: string;
city: string;
}
interface Contact {
phone: string;
}
// interface style: extends
interface Employee extends Person, Contact {
employeeId: number;
salary: number;
}
// type alias style: intersection
type Customer = Person & Contact & {
loyaltyPoints: number;
};
const kabir: Employee = {
name: "Kabir", city: "Jaipur", phone: "98290 00000", employeeId: 301, salary: 42000,
};
const priya: Customer = {
name: "Priya", city: "Pune", phone: "98220 11111", loyaltyPoints: 1250,
};
function sendSms(to: Contact & Person, text: string): void {
console.log(`SMS to ${to.name} (${to.phone}): ${text}`);
}
sendSms(kabir, "Salary credited");
sendSms(priya, `You have ${priya.loyaltyPoints} points`);SMS to Kabir (98290 00000): Salary credited SMS to Priya (98220 11111): You have 1250 points
Both styles produce a type with all the properties. Notice the parameter type Contact & Person: you can also write an intersection inline, without naming it.
There is one practical difference. When two parts disagree about a property, extends reports an error right away, while an intersection quietly produces an impossible type:
interface Item {
price: number;
}
interface SaleItem extends Item {
price: string;
}
type Mixed = Item & { price: string };
const x: Mixed = { price: 100 };app.ts(5,11): error TS2430: Interface 'SaleItem' incorrectly extends interface 'Item'.
Types of property 'price' are incompatible.
Type 'string' is not assignable to type 'number'.
app.ts(10,20): error TS2322: Type 'number' is not assignable to type 'never'.The interface error points at the real mistake. The intersection makes price both a number and a string at once, which is impossible, so its type becomes never (a type with no values) and the error only shows up later where you use it. The TypeScript team also notes that extends is checked faster than large intersections in big projects.
Declaration merging
Interfaces have one feature type aliases lack: if you declare the same interface name twice, TypeScript merges the declarations into one. A type alias name can only be declared once.
interface AppSettings {
language: string;
}
interface AppSettings {
darkMode: boolean;
}
const settings: AppSettings = { language: "hi", darkMode: true };
console.log(settings);{ language: 'hi', darkMode: true }You would rarely merge your own interfaces on purpose. Merging is useful for adding properties to types defined by someone else, such as a library or the browser's Window. You will see this in Declaration Files & DefinitelyTyped. The flip side is that an accidental second interface with the same name silently merges, while a duplicate type alias gives error TS2300: Duplicate identifier.
What only type aliases can do
An interface always describes an object shape. A type alias can name any type:
type Pincode = string; // a primitive
type PaymentMode = "upi" | "card" | "cash"; // a union of literals
type Point = [x: number, y: number]; // a tuple
type Formatter = (amount: number) => string; // a function type
type Nullable<T> = T | null; // a generic helperUnions, tuples, function types and the advanced types you will meet later (mapped and conditional types) all need type.
Which should you use?
| Feature | interface | type |
|---|---|---|
| Describe an object shape | Yes | Yes |
| Extend or combine | extends | & intersection |
| Unions, tuples, primitives, functions | No | Yes |
| Declaration merging | Yes | No (duplicate name error) |
Class can implements it | Yes | Yes (if it is an object type) |
A simple, widely used rule:
- Use
interfacefor object shapes, especially ones that other types extend or that a library exposes. - Use
typefor everything else: unions, tuples, function types and type transformations. - Above all, be consistent within a project. Both work, and mixing them randomly confuses readers.
Common mistakes
- Writing
interface Mode = "upi" | "card". Interfaces cannot describe unions; usetype. - Expecting an intersection to warn about conflicts.
A & Bwith clashing properties givesnever; preferextendsfor object inheritance. - Accidental merging. Two files that both declare a global
interface Configmerge into one type. Use modules so names do not collide. - Endless debates. For a plain object, interface or type alias makes no difference. Pick one rule and follow it.
Key points
interface Name { ... }describes an object shape, just liketype Name = { ... }.- Interfaces extend with
extends; type aliases combine with the&intersection. - Conflicting properties are an error with
extendsbut silently becomeneverwith&. - Interfaces with the same name merge; type alias names must be unique.
- Only type aliases can name unions, tuples, primitives and function types.
Exercise
Create an interface Vehicle with regNumber and wheels. Extend it into Bus (adds seats and route) and Truck (adds maxLoadKg). Then write a type alias Fleet for an array that may hold buses or trucks, and a function countSeats(fleet: Fleet): number that adds up the seats of all buses. Hint: check "seats" in v.
Show solution
interface Vehicle {
regNumber: string;
wheels: number;
}
interface Bus extends Vehicle {
seats: number;
route: string;
}
interface Truck extends Vehicle {
maxLoadKg: number;
}
type Fleet = (Bus | Truck)[];
function countSeats(fleet: Fleet): number {
let total = 0;
for (const v of fleet) {
if ("seats" in v) total += v.seats;
}
return total;
}
const depot: Fleet = [
{ regNumber: "HR12 AB 1234", wheels: 6, seats: 52, route: "Rohtak-Delhi" },
{ regNumber: "HR12 CD 5678", wheels: 10, maxLoadKg: 16000 },
{ regNumber: "HR12 EF 9012", wheels: 6, seats: 40, route: "Rohtak-Hisar" },
];
console.log(countSeats(depot)); // 92The interfaces describe object shapes, and the type alias Fleet is needed for the union. The in check tells TypeScript that v is a Bus inside the if.
Quick Quiz
1. Which can describe a union such as "upi" | "card"?
Interfaces always describe object shapes; unions need a type alias.
2. What happens when you declare interface Settings twice in the same scope?
Declaration merging combines interfaces with the same name.
3. What is the type alias equivalent of interface C extends A, B {}?
An intersection & combines the properties of both types.
4. What does an intersection of { price: number } and { price: string } give for price?
A value cannot be a number and a string at once, so the property becomes never.
Learn TypeScript with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
