0
0
Typescriptprogramming~15 mins

Polymorphism through interfaces in Typescript - Deep Dive

Choose your learning style9 modes available
Overview - Polymorphism through interfaces
What is it?
Polymorphism through interfaces means that different objects can be used in the same way because they follow the same set of rules called an interface. An interface defines what methods or properties an object should have, but not how they work inside. This lets us write code that works with many types of objects without knowing their exact details.
Why it matters
Without polymorphism through interfaces, programmers would have to write separate code for each type of object, making programs long and hard to change. This concept helps us build flexible and reusable code that can work with new types easily, saving time and reducing mistakes. It makes software easier to grow and maintain.
Where it fits
Before learning this, you should understand basic TypeScript types, functions, and classes. After this, you can explore advanced object-oriented programming concepts like abstract classes, generics, and design patterns that use polymorphism.
Mental Model
Core Idea
Polymorphism through interfaces means different objects can be treated the same way because they promise to have certain methods or properties.
Think of it like...
Imagine a power outlet interface: any device that plugs in must have the right plug shape and voltage. The outlet doesn't care if it's a lamp, phone charger, or toaster; it just expects the device to fit and work. Similarly, interfaces define what methods an object must have, so code can use any object that fits the interface.
┌───────────────┐
│   Interface   │
│ ┌───────────┐ │
│ │ methodA() │ │
│ │ methodB() │ │
│ └───────────┘ │
└─────┬─────────┘
      │
 ┌────┴─────┐   ┌───────────┐   ┌───────────┐
 │ Object1  │   │ Object2   │   │ Object3   │
 │ implements│   │ implements│   │ implements│
 │ Interface │   │ Interface │   │ Interface │
 │ methodA() │   │ methodA() │   │ methodA() │
 │ methodB() │   │ methodB() │   │ methodB() │
 └──────────┘   └───────────┘   └───────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Interfaces Basics
🤔
Concept: Learn what an interface is and how it defines a contract for objects.
In TypeScript, an interface describes the shape of an object. It lists the properties and methods an object must have. For example: interface Animal { makeSound(): void; } This means any object that says it is an Animal must have a makeSound method.
Result
You can create objects or classes that follow this interface, ensuring they have the required methods.
Understanding interfaces as contracts helps you see how TypeScript checks that objects fit expected shapes, enabling safer code.
2
FoundationImplementing Interfaces in Classes
🤔
Concept: Learn how classes can promise to follow an interface by implementing it.
Classes can use the 'implements' keyword to say they follow an interface. For example: class Dog implements Animal { makeSound() { console.log('Woof!'); } } This means Dog must have a makeSound method, or TypeScript will give an error.
Result
Classes that implement interfaces must provide the methods defined, ensuring consistent behavior.
Knowing how classes implement interfaces shows how polymorphism starts: different classes can be used where the interface is expected.
3
IntermediateUsing Interface Types for Polymorphism
🤔Before reading on: do you think you can use different class instances interchangeably if they implement the same interface? Commit to your answer.
Concept: Learn how variables typed as an interface can hold any object that implements it.
You can declare a variable with an interface type and assign it any object that follows that interface: let pet: Animal; pet = new Dog(); pet.makeSound(); // Woof! Later, you can assign a different class that also implements Animal: class Cat implements Animal { makeSound() { console.log('Meow!'); } } pet = new Cat(); pet.makeSound(); // Meow!
Result
The same variable can hold different objects, and calling the interface method runs the correct version.
This shows how polymorphism lets code work with many types through a common interface, making it flexible and reusable.
4
IntermediateInterfaces with Multiple Methods
🤔Before reading on: do you think interfaces can require more than one method? How does that affect implementing classes? Commit to your answer.
Concept: Interfaces can define many methods and properties, requiring classes to implement all of them.
For example: interface Vehicle { start(): void; stop(): void; } class Car implements Vehicle { start() { console.log('Car started'); } stop() { console.log('Car stopped'); } } All methods must be implemented, or TypeScript will show errors.
Result
Classes must fully follow the interface, ensuring consistent behavior across different implementations.
Knowing interfaces can require multiple methods helps you design clear contracts for complex behaviors.
5
AdvancedInterface Polymorphism in Function Parameters
🤔Before reading on: do you think functions can accept any object that implements an interface? Commit to your answer.
Concept: Functions can use interface types to accept any object that follows the interface, enabling polymorphic behavior.
Example: function makeAnimalSound(animal: Animal) { animal.makeSound(); } makeAnimalSound(new Dog()); // Woof! makeAnimalSound(new Cat()); // Meow! The function does not care what exact class the object is, only that it has makeSound.
Result
Functions become more flexible and reusable by working with any object that fits the interface.
This pattern is key to writing code that can handle many types without changing the function.
6
AdvancedExtending Interfaces for Complex Contracts
🤔
Concept: Interfaces can build on other interfaces to create more detailed contracts.
You can extend interfaces: interface FlyingAnimal extends Animal { fly(): void; } class Bird implements FlyingAnimal { makeSound() { console.log('Chirp'); } fly() { console.log('Flying'); } } This means Bird must implement both makeSound and fly.
Result
You can create layered interfaces that add features, helping organize complex behaviors.
Extending interfaces helps manage complexity by building on simpler contracts.
7
ExpertStructural Typing Enables Flexible Polymorphism
🤔Before reading on: do you think TypeScript requires explicit 'implements' to treat an object as an interface? Commit to your answer.
Concept: TypeScript uses structural typing, meaning objects fit an interface if they have the right shape, even without 'implements'.
Example: interface Animal { makeSound(): void; } const duck = { makeSound() { console.log('Quack'); } }; function play(animal: Animal) { animal.makeSound(); } play(duck); // Quack Here, duck has no 'implements Animal' but fits the interface by shape.
Result
This allows more flexible polymorphism without strict class hierarchies.
Understanding structural typing reveals why interfaces in TypeScript are powerful and different from some other languages.
Under the Hood
TypeScript checks interfaces at compile time by comparing the shape of objects to the interface definition. It does not add code at runtime. This means any object with the required methods and properties is accepted, regardless of explicit declarations. This is called structural typing. At runtime, JavaScript uses the actual methods on objects, so polymorphism works naturally by calling the method present on the object.
Why designed this way?
TypeScript was designed to add safety without changing JavaScript's flexible nature. Structural typing fits JavaScript's dynamic objects better than strict nominal typing. This design allows gradual typing and easier integration with existing JavaScript code, making adoption smoother.
Compile time check:
┌───────────────┐
│ Interface     │
│ { methodA() } │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Object shape  │
│ { methodA() } │
└──────┬────────┘
       │
       ▼
  Matches? Yes → OK

Runtime call:
┌───────────────┐
│ Object        │
│ methodA()     │
└──────┬────────┘
       │
       ▼
  methodA runs actual code
Myth Busters - 4 Common Misconceptions
Quick: Does an object need to explicitly say 'implements Interface' to be treated as that interface? Commit yes or no.
Common Belief:An object must explicitly declare 'implements Interface' to be considered as that interface.
Tap to reveal reality
Reality:TypeScript uses structural typing, so any object with the right shape fits the interface, even without 'implements'.
Why it matters:Believing explicit declaration is required can limit flexibility and cause confusion when objects work without it.
Quick: Can interfaces contain actual code implementations? Commit yes or no.
Common Belief:Interfaces can have code inside methods to share behavior.
Tap to reveal reality
Reality:Interfaces only describe method signatures and properties; they cannot contain code implementations.
Why it matters:Expecting code in interfaces leads to errors and misunderstanding of how to share behavior (use classes or abstract classes instead).
Quick: Does polymorphism mean objects must share the same class? Commit yes or no.
Common Belief:Polymorphism requires objects to be instances of the same class or subclass.
Tap to reveal reality
Reality:Polymorphism through interfaces allows objects of different classes to be used interchangeably if they follow the same interface.
Why it matters:Thinking polymorphism needs class inheritance limits design options and misses the power of interfaces.
Quick: Does TypeScript check interfaces at runtime? Commit yes or no.
Common Belief:TypeScript enforces interfaces during program execution to prevent errors.
Tap to reveal reality
Reality:Interface checks happen only at compile time; at runtime, JavaScript runs without interface enforcement.
Why it matters:Expecting runtime checks can cause false confidence and bugs if runtime objects don't match interfaces.
Expert Zone
1
Interfaces in TypeScript are purely compile-time constructs and do not exist in the emitted JavaScript, so runtime behavior depends entirely on actual object methods.
2
Structural typing allows objects to be polymorphic even if they come from unrelated classes or plain objects, enabling flexible design patterns like duck typing.
3
Extending interfaces can create complex type hierarchies, but overusing them can lead to brittle code that is hard to maintain or understand.
When NOT to use
Avoid interfaces when you need runtime type information or behavior sharing; use abstract classes or classes instead. Also, if your types require nominal typing (exact type identity), interfaces may not suffice.
Production Patterns
In real-world TypeScript projects, interfaces are used to define APIs, data shapes, and service contracts. Polymorphism through interfaces enables dependency injection, mocking in tests, and flexible module design where components can be swapped easily.
Connections
Duck Typing (Programming)
Polymorphism through interfaces in TypeScript is a form of duck typing based on object shape.
Knowing duck typing helps understand why TypeScript accepts objects without explicit declarations if they have the right methods.
Contracts in Law
Interfaces act like contracts that parties agree to follow, defining obligations without dictating how to fulfill them.
Understanding legal contracts clarifies why interfaces specify 'what' but not 'how', enabling different implementations.
Plug and Socket Electrical Systems
Interfaces are like plugs that fit sockets; any device with the right plug shape can connect and work.
This connection shows how polymorphism allows many different objects to be used interchangeably if they fit the interface.
Common Pitfalls
#1Assuming an object must declare 'implements' to fit an interface.
Wrong approach:interface Animal { makeSound(): void; } const obj = { makeSound() { console.log('Hi'); } }; function greet(animal: Animal) { animal.makeSound(); } greet(obj); // Error: obj does not implement Animal
Correct approach:interface Animal { makeSound(): void; } const obj = { makeSound() { console.log('Hi'); } }; function greet(animal: Animal) { animal.makeSound(); } greet(obj); // Works fine
Root cause:Misunderstanding that TypeScript uses structural typing, not nominal typing.
#2Trying to put method code inside an interface.
Wrong approach:interface Animal { makeSound() { console.log('Sound'); } // Error: Interfaces can't have implementations }
Correct approach:interface Animal { makeSound(): void; } class Dog implements Animal { makeSound() { console.log('Woof'); } }
Root cause:Confusing interfaces with classes or abstract classes that can have method bodies.
#3Using interface types but passing objects missing required methods.
Wrong approach:interface Animal { makeSound(): void; } const obj = { }; // no makeSound function greet(animal: Animal) { animal.makeSound(); } greet(obj); // Runtime error: makeSound is not a function
Correct approach:interface Animal { makeSound(): void; } const obj = { makeSound() { console.log('Hi'); } }; function greet(animal: Animal) { animal.makeSound(); } greet(obj); // Works safely
Root cause:Ignoring that TypeScript only checks types at compile time; runtime objects must have the methods.
Key Takeaways
Polymorphism through interfaces lets different objects be used the same way by sharing a common set of methods or properties.
TypeScript uses structural typing, so objects fit interfaces by having the right shape, not by explicit declarations.
Interfaces only describe what methods or properties an object must have, not how they work inside.
Using interfaces in function parameters enables flexible and reusable code that works with many object types.
Understanding these concepts helps write safer, clearer, and more maintainable TypeScript programs.