Skip to main content

TS Ep 20: Implementing Interfaces (`implements` Keyword)

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
typescript - This article is part of a series.
Part 20: This Article
An interface defines a public shape or contract. The implements keyword allows a class to declare that it fulfills that contract. This enables Polymorphism and the Dependency Inversion Principle, ensuring disparate classes expose identical, predictable APIs.

1. The implements Contract Validation
#

When a class declares implements InterfaceName, TypeScript acts as a strict compliance inspector. It verifies that the class implements every public field and method specified by the interface.

interface PaymentGateway {
  name: string;
  processPayment(amount: number, currency: string): Promise<boolean>;
  refund(transactionId: string): Promise<boolean>;
}

// 🟢 StripeGateway promises to implement PaymentGateway
class StripeGateway implements PaymentGateway {
  public name = "Stripe";

  public async processPayment(amount: number, currency: string): Promise<boolean> {
    console.log(`Processing Stripe payment of ${amount} ${currency}`);
    return true;
  }

  public async refund(transactionId: string): Promise<boolean> {
    console.log(`Refunding Stripe transaction: ${transactionId}`);
    return true;
  }
}

What Happens When a Contract is Broken?
#

If a class fails to implement even one required method, or provides incorrect parameter types, TypeScript rejects compilation:

// ❌ Compiler Error: Class 'PayPalGateway' incorrectly implements interface 'PaymentGateway'.
// Property 'refund' is missing in type 'PayPalGateway' but required in type 'PaymentGateway'.
class PayPalGateway implements PaymentGateway {
  public name = "PayPal";

  public async processPayment(amount: number, currency: string): Promise<boolean> {
    return true;
  }
  // Forgot refund() method!
}

2. A Crucial Misconception: implements Does NOT Infer Method Parameter Types!
#

A very common surprise for TypeScript developers is discovering that writing implements Interface does NOT automatically infer parameter types for the class methods!

interface Logger {
  log(message: string): void;
}

class ConsoleLogger implements Logger {
  // ❌ Compiler Error: Parameter 'message' implicitly has an 'any' type.
  // TypeScript DOES NOT automatically copy parameter types from the interface to the class method body!
  /*
  log(message) {
    console.log(message);
  }
  */

  // 🟢 CORRECT: You MUST still explicitly annotate method parameters inside the class!
  log(message: string): void {
    console.log(`[LOG]: ${message}`);
  }
}
Important

The Inspection Rule: The implements clause is purely a validation check, NOT a type transformer. It checks your class after it is typed; it does not inject types into method signatures.


3. Implementing Multiple Interfaces
#

Unlike classical inheritance (where a class can only extend one parent class), a TypeScript class can implement an arbitrary number of interfaces separated by commas:

interface Serializable {
  serialize(): string;
}

interface Authenticatable {
  verifyToken(token: string): boolean;
}

interface Auditable {
  readonly createdAt: Date;
}

// Fulfills ALL 3 Contracts simultaneously
class SecureUserSession implements Serializable, Authenticatable, Auditable {
  public readonly createdAt = new Date();

  constructor(public readonly userId: string) {}

  public serialize(): string {
    return JSON.stringify({ userId: this.userId, createdAt: this.createdAt });
  }

  public verifyToken(token: string): boolean {
    return token.startsWith(`usr_${this.userId}_`);
  }
}

4. Public Contracts Only Rule
#

Interfaces can only declare the public shape of an object. You cannot declare private or protected members in an interface.

interface DatabaseConnection {
  // ❌ Compiler Error: 'private' modifier cannot appear on a type member.
  // private connectionString: string; 

  connect(): void;
  disconnect(): void;
}

An interface represents how the outside world interacts with instances of your class. The internal private backing implementation is an internal concern of the class itself:

class PostgresConnection implements DatabaseConnection {
  // Private property added independently by the class to support internal logic
  private connectionString: string;

  constructor(uri: string) {
    this.connectionString = uri;
  }

  public connect(): void {
    console.log(`Connecting to ${this.connectionString}`);
  }

  public disconnect(): void {
    console.log("Disconnected.");
  }
}

5. Polymorphism & The Dependency Inversion Principle
#

Why do we bother writing interfaces and implements clauses?

It enables the Dependency Inversion Principle (DIP) (the “D” in SOLID design principles). High-level business logic should depend on abstractions (Interfaces), not concrete implementations (Classes).

// High-Level Business Processor depends on the PaymentGateway Interface, NOT Stripe or PayPal!
class CheckoutService {
  constructor(private gateway: PaymentGateway) {}

  public async completeOrder(orderId: string, total: number) {
    console.log(`[Checkout]: Processing Order #${orderId}`);
    const isSuccess = await this.gateway.processPayment(total, "USD");
    if (!isSuccess) throw new Error("Payment failed.");
  }
}

// We can seamlessly inject ANY class that implements PaymentGateway:
const stripeCheckout = new CheckoutService(new StripeGateway());

Summary & Next Steps
#

In this episode:

  • We learned how implements validates class structural compliance at compile time.
  • We uncovered the Inspection Rule: implements checks your class, but does NOT automatically infer parameter types on method bodies.
  • We implemented multiple interfaces on a single class (implements A, B, C).
  • We reinforced the Public Contract Rule (interfaces only declare public APIs).
  • We demonstrated Polymorphism and the Dependency Inversion Principle.

In Episode 21: Inheritance and the super Keyword, we will explore class inheritance (extends), method overriding, and constructor chaining with super()!

typescript - This article is part of a series.
Part 20: This Article