CodePark/ questions / Polymorphism: classes vs unions
START PRACTICINGPRACTICE
Backend question · Object-oriented programming › Polymorphism: classes vs unions

You can model payment methods as classes behind an interface, or as a discriminated union handled with switch. How do you choose?

// Option A: classes
interface PaymentMethod { fee(amount: number): number }
class Card implements PaymentMethod { fee(a: number) { return a * 0.029; } }

// Option B: discriminated union
type Payment = { kind: "card"; last4: string } | { kind: "bank"; iban: string };
function fee(p: Payment, a: number): number {
  switch (p.kind) { case "card": return a * 0.029; case "bank": return 0.5; }
}

Ask: what changes more often, the kinds or the operations?

A discriminated union is a union type where every member has a shared tag field, like kind. TypeScript narrows the type inside each case.

Classes behind an interface (Option A) are best when you add new kinds often. A new provider is one new class, and existing code doesn't change. They also fit when each kind has its own dependencies (an SDK client, config) or state.

A union with switch (Option B) is best when the kinds are a fixed set and you add new operations often: fee, label, refundRules. Each operation is one function. Other good points:

  • The data is plain, so it works with JSON, databases and queues with no conversion.
  • With an exhaustive check, TypeScript shows every switch you must update when a new kind is added.

The trade-off between these two is called the expression problem.

In backends: classes for adapters to outside systems (payment providers, storage). Unions for domain states and messages (order status, events).

  • Classes make it easy to add new kinds without editing existing code.
  • Unions with switch make it easy to add new operations over a fixed set of kinds.
  • Union values are plain data, so they serialize without conversion.
  • An exhaustive check makes the compiler list every switch to update.
  • Use classes for external adapters and unions for domain states and messages.