A class with a `kind` or `type` field (string or enum) plus methods that switch on it — `Employee` carrying a `type` field used to dispatch `bonus()` to different formulas.
A 'kind' string field becomes a real subclass type; the type system enforces the legal set.
Before the refactoring
class Employee {type; // 'engineer' | 'manager'bonus() {switch (this.type) {case 'engineer': return this.salary * 0.10;case 'manager': return this.salary * 0.15 + this.reports.length * 100;}}}
After the refactoring
class Employee {}class Engineer extends Employee {bonus() { return this.salary * 0.10; }}class Manager extends Employee {bonus() { return this.salary * 0.15 + this.reports.length * 100; }}
Every method that varies by type must switch; the compiler can't enforce that all kinds are handled; new kinds require updating every switch.
If only one or two switches exist on the type code, subclassing is over-design; combine with Replace Conditional with Polymorphism only when dispatch repeats.
Compile-time checks that no kind is missed; per-kind behavior lives where it belongs.
Replacing the type code with subclasses when only one or two switches exist on it — over-design for a coordination point that's barely a smell.