Core JavaScript · 6 of 9
Inheritance ends up with a base class with shared logic and subclasses overriding bits of it. Therefore each subclass's real behavior is scattered across multiple classes and files.
class BaseValidator {
validate(value: string) {
return this.check(value);
}
protected check(value: string) {
return value.length > 0;
}
}
class EmailValidator extends BaseValidator {
protected check(value: string) {
if (value.includes("@")) {
return super.check(value);
}
return false
}
}
const notEmpty = (value) => value.length > 0;
const hasAt = (value) => value.includes("@");
const isValidEmail = (value) => notEmpty(value) && hasAt(value);
isValidEmail("a@b.com"); // true
Each function does one thing and can be tested and reused on its own. isValidEmail just calls the checks it needs and combines them with plain && — no generic pipe/compose helper to reach for, no guessing what order functions run in or what shape they're expected to return. There's no hierarchy to climb to see what a given check actually does, and no risk of a subclass's override silently changing behavior a base class assumed elsewhere.