Bird
Raised Fist0
Typescriptprogramming~15 mins

Polymorphism through interfaces in Typescript - Deep Dive

Choose your learning style10 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.

Practice

(1/5)
1. What does polymorphism through interfaces allow in TypeScript?
easy
A. Only one class can implement an interface
B. Different classes to be treated the same if they implement the same interface
C. Interfaces can hold data like variables
D. Classes cannot implement more than one interface

Solution

  1. Step 1: Understand polymorphism concept

    Polymorphism means using different objects in the same way if they share a common interface.
  2. Step 2: Apply to interfaces in TypeScript

    Interfaces define methods that classes promise to implement, allowing different classes to be used interchangeably.
  3. Final Answer:

    Different classes to be treated the same if they implement the same interface -> Option B
  4. Quick Check:

    Polymorphism = Same interface, different classes [OK]
Hint: Polymorphism means same interface, different class objects [OK]
Common Mistakes:
  • Thinking interfaces hold data like variables
  • Believing only one class can implement an interface
  • Assuming classes can't implement multiple interfaces
2. Which of the following is the correct syntax for a TypeScript interface with a method speak() that returns a string?
easy
A. interface Animal { speak(): string; }
B. interface Animal { speak: string(); }
C. interface Animal { speak() => string; }
D. interface Animal { speak: () => string; }

Solution

  1. Step 1: Review interface method syntax

    In TypeScript, methods in interfaces are declared as methodName(): returnType;.
  2. Step 2: Check each option

    interface Animal { speak(): string; } correctly uses speak(): string;. interface Animal { speak: string(); } incorrectly places parentheses after the type. interface Animal { speak() => string; } uses arrow syntax wrongly. interface Animal { speak: () => string; } uses a function type, which is valid but less common for method declarations.
  3. Final Answer:

    interface Animal { speak(): string; } -> Option A
  4. Quick Check:

    Method syntax = methodName(): returnType [OK]
Hint: Method in interface uses colon after name, not arrow [OK]
Common Mistakes:
  • Using arrow syntax inside interface method
  • Putting parentheses after type instead of method name
  • Confusing function type with method declaration
3. What will be the output of this TypeScript code?
interface Shape {
  area(): number;
}

class Square implements Shape {
  constructor(private side: number) {}
  area() { return this.side * this.side; }
}

class Circle implements Shape {
  constructor(private radius: number) {}
  area() { return Math.PI * this.radius * this.radius; }
}

function printArea(shape: Shape) {
  console.log(shape.area().toFixed(2));
}

const sq = new Square(3);
const cir = new Circle(2);
printArea(sq);
printArea(cir);
medium
A. Error: area method missing
B. 6 and 12.56
C. 9.00 and 12.57
D. 9 and 12.56

Solution

  1. Step 1: Calculate area of Square

    Square side is 3, so area = 3 * 3 = 9. Output with two decimals is "9.00".
  2. Step 2: Calculate area of Circle

    Circle radius is 2, area = π * 2 * 2 ≈ 12.566. Rounded to two decimals is "12.57".
  3. Final Answer:

    9.00 and 12.57 -> Option C
  4. Quick Check:

    Square area = 9.00, Circle area ≈ 12.57 [OK]
Hint: Calculate areas, then round to 2 decimals [OK]
Common Mistakes:
  • Forgetting to round with toFixed(2)
  • Using wrong formula for circle area
  • Confusing radius and side length
4. Identify the error in this TypeScript code using interfaces and polymorphism:
interface Vehicle {
  move(): void;
}

class Car implements Vehicle {
  move(distance: number) {
    console.log(`Car moved ${distance} meters`);
  }
}

const myCar: Vehicle = new Car();
myCar.move();
medium
A. Missing return type in move method
B. Cannot assign new Car() to Vehicle type
C. Interface Vehicle cannot have methods
D. Method move in Car has a different signature than in Vehicle interface

Solution

  1. Step 1: Compare method signatures

    Interface Vehicle defines move() with no parameters. Car's move method requires a number parameter.
  2. Step 2: Understand TypeScript method compatibility

    Method signatures must match exactly for interface implementation. Car's move method does not match Vehicle's move method.
  3. Final Answer:

    Method move in Car has a different signature than in Vehicle interface -> Option D
  4. Quick Check:

    Method signature mismatch = Error [OK]
Hint: Method signatures must match interface exactly [OK]
Common Mistakes:
  • Thinking parameter difference is allowed
  • Believing assignment to interface type is invalid
  • Ignoring method return type requirements
5. You have an interface Logger with a method log(message: string): void. You want to create two classes: ConsoleLogger that logs to the console, and FileLogger that stores logs in an array. How can you use polymorphism to write a function writeLog(logger: Logger, msg: string) that works with both classes? Which implementation is correct?
hard
A. function writeLog(logger: Logger, msg: string) { logger.log(msg); }
B. function writeLog(logger: Logger, msg: string) { console.log(msg); }
C. function writeLog(logger: Logger, msg: string) { logger.write(msg); }
D. function writeLog(logger: Logger, msg: string) { logger.log(); }

Solution

  1. Step 1: Understand polymorphism with interfaces

    The function should accept any object implementing Logger and call its log method with the message.
  2. Step 2: Check each function implementation

    function writeLog(logger: Logger, msg: string) { logger.log(msg); } calls logger.log(msg) correctly. function writeLog(logger: Logger, msg: string) { console.log(msg); } ignores the passed logger and logs directly to console. function writeLog(logger: Logger, msg: string) { logger.write(msg); } calls a non-existent method write. function writeLog(logger: Logger, msg: string) { logger.log(); } calls log without arguments, which is incorrect.
  3. Final Answer:

    function writeLog(logger: Logger, msg: string) { logger.log(msg); } -> Option A
  4. Quick Check:

    Call interface method with correct argument [OK]
Hint: Call the interface method with the message parameter [OK]
Common Mistakes:
  • Calling wrong method name
  • Not passing the message argument
  • Ignoring the logger parameter
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard