TypeScript how does the “Implements” keyword work?`

Key Concepts of TypeScript Interfaces and implements

  1. Interface as a Contract:
  2. An interface in TypeScript defines a contract that any implementing class must adhere to.
  3. It specifies a set of methods and properties without providing implementations.
  4. Interfaces ensure that classes provide certain behaviors, but they don’t limit classes to only those behaviors.
  5. interface CharacterRepository {
      getCharacters(page: number): Promise<Character[]>;
      updateCharacter(characterId: string, updatedData: Partial<Character>): Promise<Character>;
    }
  6. Concrete Implementation:
  7. A class uses the implements keyword to declare that it satisfies the contract defined by an interface.
  8. The class must provide concrete implementations for all the methods and properties defined in the interface.
  9. It can also have additional methods and properties that are not part of the interface.
  10. class CharacterRelayRepository implements CharacterRepository {
      async getCharacters(page: number): Promise<Character[]> {
        // Implementation of fetching characters
      }
    
      async updateCharacter(characterId: string, updatedData: Partial<Character>): Promise<Character> {
        // Implementation of updating a character
      }
    
      async deleteCharacter(characterId: string): Promise<boolean> {
        // Additional method not required by the interface
      }
    }
    
    
  11. TypeScript's Structural Typing:
  12. TypeScript uses structural typing (or "duck typing"), meaning an object satisfies an interface if it has all the required properties and methods.
  13. A class can have more properties and methods than those defined in the interface, and TypeScript will not raise an error.
  14. This allows for flexibility and extensibility, enabling classes to offer additional functionality without breaking the interface contract.
  15. Dependency Injection and Abstraction:
  16. By relying on interfaces, classes can be more easily decoupled from specific implementations, promoting abstraction.
  17. Dependency injection allows classes to be initialized with specific implementations that adhere to an interface, enabling flexible design patterns like Clean Architecture.
  18. This design ensures that business logic can work with any class implementing the interface, providing ease of testing and flexibility.
  19. class GetCharacterList {
      private characterRepository: CharacterRepository;
    
      constructor(characterRepository: CharacterRepository) {
        this.characterRepository = characterRepository;
      }
    
      async execute(page: number): Promise<Character[]> {
        return this.characterRepository.getCharacters(page);
      }
    }
    
    

Practical Benefits

  1. Flexibility:
  2. You can change or extend the implementation details without modifying the code that depends on the interface.
  3. Testability:
  4. Interfaces allow for easy mocking in tests, as you can create mock classes implementing the interface without changing the test setup.
  5. Separation of Concerns:
  6. Interfaces allow business logic to be independent of specific data sources or implementations, adhering to the Dependency Inversion Principle.
  7. Polymorphism:
  8. Multiple classes can implement the same interface, enabling polymorphism. You can treat instances of different classes as instances of the interface type.

Example for Clarity

Consider a scenario where you have a Vehicle interface and multiple classes implementing it:

interface Vehicle {
  drive(): void;
  stop(): void;
}

class Car implements Vehicle {
  drive() {
    console.log("Car driving");
  }

  stop() {
    console.log("Car stopped");
  }

  honk() {
    console.log("Car honking");
  }
}

class Bike implements Vehicle {
  drive() {
    console.log("Bike driving");
  }

  stop() {
    console.log("Bike stopped");
  }
}

function testDrive(vehicle: Vehicle) {
  vehicle.drive();
  vehicle.stop();
}

const myCar = new Car();
const myBike = new Bike();

testDrive(myCar); // Car driving, Car stopped
testDrive(myBike); // Bike driving, Bike stopped

Explanation:

  • Interface Definition: Vehicle specifies that implementing classes must have drive and stop methods.
  • Concrete Implementations: Both Car and Bike implement Vehicle, providing specific implementations for drive and stop.
  • Additional Methods: Car has an additional method honk, which doesn't affect its conformance to the Vehicle interface.
  • Usage: The testDrive function can operate on any Vehicletype object, demonstrating polymorphism and the flexibility of using interfaces.

Click here to share this article with your friends on X if you liked it.