Extending Interfaces
Falaknuma extends Chowmahalla — inherit the grandeur, add your own shine.
In the real world, complex shapes are built by layering simpler ones. A "Luxury Palace" isn't drawn from scratch — it starts as a "Palace" and then adds extra grand features. A "HITEC City Office" begins as an "Office" and layers on startup perks like bean bags and espresso machines. TypeScript models this exact pattern with extends — one interface can inherit every property from another and then add its own.
Think of the Nizam's Hyderabad. First, he built Chowmahalla Palace — grand halls, fountains, courtyards. The blueprint was magnificent. Years later, he wanted Falaknuma Palace. Falaknuma has everything Chowmahalla has — grand halls, fountains, courtyards — PLUS a stunning durbar hall and a world-famous dining table that seats 100 guests.
Falaknuma extends Chowmahalla. It inherits every feature from the base palace and adds its own. The architect didn't redraw the entire Chowmahalla blueprint from scratch. He simply said: "Start with Chowmahalla, and add these new things on top." That's exactly what interface extension does in TypeScript — you define a base shape once, and then build more specific shapes on top of it without repeating yourself.
// Falaknuma inherits Chowmahalla's
// blueprint and adds its own features
interface Chowmahalla {
halls: number;
fountains: number;
courtyards: number;
}
// Falaknuma gets halls, fountains,
// courtyards PLUS durbar & dining
interface Falaknuma extends Chowmahalla {
durbarHall: boolean;
diningSeats: number;
}
The child interface gets all the parent's properties automatically and can add more of its own. Just like Falaknuma didn't need to rebuild Chowmahalla's halls — they were already in the blueprint — your child interface doesn't redeclare the parent's properties. It simply inherits them and layers on new ones.
The syntax for extending a single interface is straightforward: interface Child extends Parent { ... }. The child interface automatically inherits every property from the parent and can add its own new properties. Let's see this in action with a clear example.
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
// Dog now has: name, age,
// breed, bark()
Now Dog has all four members: name and age inherited from Animal, plus breed and bark() that it adds itself. This means a Dog can be used wherever an Animal is expected — because every Dog IS an Animal with extra capabilities.
function describe(a: Animal): string {
return a.name + ", age " + a.age;
}
const myDog: Dog = {
name: "Sheru",
age: 3,
breed: "Labrador",
bark() { console.log("Woof!"); }
};
// This works! Dog has everything
// Animal requires
describe(myDog); // "Sheru, age 3"
You can also re-declare a property from the parent in the child, but the type must be compatible — it must be a subtype of the parent's type. You cannot widen or change it to something incompatible.
interface BasicUser {
id: string;
name: string;
}
// name re-declared with same type:
// OK, string is a subtype of string
interface PremiumUser extends BasicUser {
name: string;
subscription: string;
}
// ERROR! number is NOT a subtype
// of string
interface BadUser extends BasicUser {
name: number;
}
When you create an object of the child type, you must include all inherited properties — you cannot skip them. The child inherits the parent's contract in full, and every property from the parent is still required. Missing even one inherited property will trigger a TypeScript error at compile time.
interface Base {
id: string;
}
interface Extended extends Base {
name: string;
}
// ERROR! Missing 'id' from Base
const obj: Extended = {
name: "Imran"
};
// Correct — includes inherited id
const correct: Extended = {
id: "abc-123",
name: "Imran"
};
Interfaces can extend more than one interface at the same time! The syntax uses commas: interface C extends A, B { ... }. The child inherits properties from all parent interfaces simultaneously. This is incredibly powerful for composing complex types from small, focused building blocks.
Think of the Hussain Sagar area — it's a lake AND a park AND a tourist spot. It combines all three aspects into one rich experience. You don't build three separate things; one place embodies all three roles. That's multiple extension in action.
interface HasId {
id: string;
}
interface HasTimestamps {
createdAt: Date;
updatedAt: Date;
}
interface Entity extends HasId,
HasTimestamps {
name: string;
}
// Entity has: id, createdAt,
// updatedAt, name
This is the composition pattern — small, focused interfaces that combine into bigger, more capable ones. Each parent interface handles one concern, and the child brings them all together. Let's see another real-world pattern:
interface Serializable {
toJSON(): string;
}
interface Loggable {
log(): void;
}
interface UserRecord extends
Serializable, Loggable {
name: string;
email: string;
}
// UserRecord has: toJSON, log,
// name, email
But there's a catch with multiple extension: if two parent interfaces declare the same property name with different types, the child must resolve the conflict. The property type must be compatible with BOTH parents, otherwise TypeScript throws an error.
interface A {
x: string;
}
interface B {
x: number;
}
// ERROR! x can't be both string
// AND number at the same time
interface C extends A, B {}
The conflict is unresolvable here — no type can be both string and number simultaneously. But if both parents have the same property with the same type, or with compatible types, it works fine:
interface First {
name: string;
}
interface Second {
name: string;
}
// OK! Both agree on name: string
interface Combined extends First,
Second {
extra: boolean;
}
Extension is powerful, but it comes with traps that catch developers off guard. Let's walk through the most common mistakes so you don't fall into them.
Trap 1: Forgetting inherited properties when creating objects. When you create an object of a child interface, you must include ALL properties — both the child's own AND every inherited property from all parent interfaces. There is no free pass to skip the parent's requirements.
interface Base {
id: string;
}
interface Extended extends Base {
name: string;
}
// ERROR! Missing 'id' from Base
const obj: Extended = {
name: "Imran"
};
// Must include ALL inherited props
const correct: Extended = {
id: "abc-123",
name: "Imran"
};
Trap 2: Type incompatibility when overriding properties. You can re-declare a parent property in the child, but only with a narrower (subtype) type. Narrowing works: string | number to string is fine. Widening or switching to an incompatible type fails.
// NARROWING — works!
interface Parent {
data: string | number;
}
interface Child extends Parent {
data: string; // OK, narrower
}
// WIDENING — ERROR!
interface Base {
data: string;
}
interface Bad extends Base {
data: number; // ERROR!
// number is not subtype
// of string
}
Trap 3: Extending from a union type alias. You CAN extend a type alias that represents an object type, but you CANNOT extend a union type alias. TypeScript only allows extending object-shaped types.
// This WORKS — object type alias
type Animal = {
name: string;
};
interface Dog extends Animal {
breed: string; // OK!
}
// This FAILS — union type alias
type Thing = A | B;
interface C extends Thing {}
// ERROR! Cannot extend union
Trap 4: Circular extension. If interface A extends B, and B extends A, TypeScript catches the circular reference immediately. This creates an infinite loop and is never valid.
// ERROR! Circular reference
interface A extends B {}
interface B extends A {}
// A needs B, B needs A...
// Infinite loop, bhai!
Let's consolidate everything we've learned about extending interfaces into a quick-reference cheatsheet you can come back to anytime.
Single Extension:
interface Child extends Parent {
// inherits all Parent props
// adds own props here
}
Multiple Extension:
interface Child extends A, B, C {
// inherits from ALL parents
// adds own props here
}
Override Rule: A child property must be a subtype of the parent property. You can narrow, but never widen or switch to an incompatible type.
// OK — narrowing
interface P { data: string | number }
interface C extends P { data: string }
// ERROR — incompatible
interface P2 { data: string }
interface C2 extends P2 { data: number }
Key Rules to Remember:
- Child inherits ALL parent properties — no exceptions, no skipping
- You can extend multiple interfaces with commas:
extends A, B - Conflicting property types in multiple parents = compiler error
- Child must include all inherited props when creating objects
- Extending type aliases works for object types, not for unions
extendsis for IS-A relationships — Dog IS AN Animal, Falaknuma IS A Palace
Real-World Composition Pattern:
interface HasId { id: string }
interface HasTimestamps {
createdAt: Date;
updatedAt: Date;
}
interface HasSoftDelete {
deleted: boolean;
}
// Compose from small pieces!
interface Entity extends HasId,
HasTimestamps, HasSoftDelete {
name: string;
}
The golden rule: "Extending interfaces is building Falaknuma on top of Chowmahalla — inherit the grandeur, add your own shine. But you can't change the foundation, bhai!"
Key Takeaways
- Use extends to make a child interface inherit all parent properties
- Multiple parents: interface C extends A, B { }
- Child property types must be subtypes of parent property types
- You must include all inherited properties when creating objects
- Cannot extend union type aliases — only object-shaped types
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login