Abstract Classes
Abstract Classes
Level 10 — Classes & OOP in TypeScript A special type of Class that acts as a foundational blueprint. It cannot be instantiated directly with
new; it exists only to be inherited by other classes.
1. Prerequisites
- Classes Overview — The standard class structure.
- Interfaces — A similar concept used for blueprinting.
2. Term Category
Object-Oriented Programming (Abstract Class Base Contracts): Abstract classes (abstract class) define partial base class implementations with un-implemented abstract methods that subclasses must implement.
3. Explanation
Environment Context
- Compile-Time (Compiles to standard JS Classes)
(1) Design Motivation — "Why did we design this?"
Imagine you are building a game. You create a Character class with health, speed, and a move() method.
You create Player and Enemy classes that inherit from Character.
But what happens if a junior developer writes const c = new Character()? What is a raw "Character"? It makes no sense in the game world. It should only be a Player or an Enemy.
Abstract Classes solve this. You mark Character as abstract. Now, it contains all the shared code, but the compiler forbids anyone from instantiating it directly.
(2) The abstract Keyword
You place abstract before the class keyword.
abstract class Character {
constructor(public name: string, public health: number) {}
takeDamage(amount: number) {
this.health -= amount;
}
}
// ❌ Error: Cannot create an instance of an abstract class.
const char = new Character("Base", 100);
// ✅ Valid: We inherit from the abstract class
class Player extends Character {
jump() { console.log("Jumping!"); }
}
const p = new Player("Hero", 100);
p.takeDamage(10); // Inherited from the abstract class!
(3) Abstract Methods
The true superpower of Abstract Classes is Abstract Methods. You can define a method signature without a body, forcing all child classes to write their own custom implementation for that method!
abstract class Character {
// We don't know HOW a character attacks, but we mandate that they MUST have an attack method!
abstract attack(): void;
}
// ❌ Error: Non-abstract class 'Player' does not implement inherited abstract member 'attack' from class 'Character'.
class Player extends Character { }
// ✅ Valid: Player implements the mandated method!
class Enemy extends Character {
attack() { console.log("Enemy bites!"); }
}
4. Common Mistakes & Pitfalls
Mistake 1: Using Abstract Classes instead of Interfaces
The mistake: A developer writes a massive Abstract Class with 10 abstract methods and 0 actual implementation code, just to act as a shape guide.
Why it's wrong: If your Abstract Class contains no actual logic (no standard methods or properties with values), you are just writing an Interface! Abstract Classes compile into real JavaScript code (they cost bytes in your bundle). Interfaces are erased completely at compile time (0 cost).
Golden Rule: If you only need to define a shape, use an interface. If you need to share actual logic/code (like the takeDamage method above) AND mandate specific shapes, use an abstract class.
Mistake 2: Attempting Direct Instantiation of Abstract Classes via new
The mistake: Writing const instance = new AbstractClass(); (TS2511).
Why it's wrong: Abstract classes serve strictly as base contracts for derived subclasses. TypeScript forbids direct instantiation of abstract classes.
Incorrect:
abstract class Logger { abstract log(msg: string): void; }
// const logger = new Logger(); // ❌ Cannot create an instance of an abstract class
Fix:
abstract class Logger { abstract log(msg: string): void; }
class ConsoleLogger extends Logger { log(msg: string) { console.log(msg); } }
const logger = new ConsoleLogger(); // Instantiate concrete subclass
Mistake 3: Omitting Abstract Method Implementations in Concrete Subclasses
The mistake: Extending an abstract class without implementing all declared abstract methods.
Why it's wrong: Concrete subclasses MUST implement every abstract method inherited from parent abstract classes.
Incorrect:
abstract class Base { abstract render(): void; }
// class Child extends Base {} // ❌ Non-abstract class 'Child' does not implement inherited abstract member 'render'
Fix:
abstract class Base { abstract render(): void; }
class Child extends Base { render() { /* Concrete implementation */ } }
5. Practice Exercises
Exercise 1: Authoring Abstract Base Classes
Scenario:
Define an abstract class Logger with a concrete log(msg) method and an abstract write(msg) method.
Requirements:
- Declare
abstract class Logger. - Extend
LoggerinConsoleLogger.
Answer
Implementation
abstract class Logger {
log(msg: string): void {
const timestamp = new Date().toISOString();
this.write(`[${timestamp}] ${msg}`);
}
abstract write(formattedMsg: string): void;
}
class ConsoleLogger extends Logger {
write(formattedMsg: string): void {
console.log(formattedMsg);
}
}
const logger = new ConsoleLogger();
logger.log("System initialized");
Technical Explanation
abstract classcannot be instantiated directly withnew Logger().abstract write()forces derived subclasses (ConsoleLogger) to supply concrete implementations.- Shared method logic (
log) is reused across all subclass implementations.
Exercise 2: Auditing Direct Abstract Instantiation Errors
Scenario:
Demonstrate compile error when attempting to instantiate an abstract class with new.
Requirements:
- Show compile error on
new AbstractClass().
Answer
Implementation
abstract class Animal {
abstract makeSound(): void;
}
// ❌ Compile Error: Cannot create an instance of an abstract class!
// const a = new Animal();
class Dog extends Animal {
makeSound(): void { console.log("Woof!"); }
}
const d = new Dog(); // Valid!
Technical Explanation
- Abstract classes serve strictly as architectural base blueprints.
- TypeScript prevents direct instantiation of un-implemented abstract classes at compile time.
- Enforces object inheritance hierarchy design.
Exercise 3: Comparative Analysis: abstract class vs interface
Scenario:
Formulate an architectural selection matrix comparing abstract class against interface.
Requirements:
- Contrast runtime JavaScript output, concrete code sharing, and inheritance limits.
Answer
Implementation
Abstract Class vs Interface Selection Matrix:
- abstract class: Preserved in transpiled JS output as a class. Supports shared concrete code execution and constructor state. Class can extend ONLY ONE abstract class (single inheritance).
- interface: Erased completely in transpiled JS output (0 bytes). Pure type contract (zero executable code). Class can implement MULTIPLE interfaces (multiple inheritance).
Technical Explanation
- Abstract classes are suitable when sharing reusable code logic across related subclasses.
- Interfaces are suitable for pure type contracts and multi-interface class capability composition.
- Fundamental OOP architectural choice.
6. Related Terms
- Interfaces — The zero-cost alternative for pure shape blueprinting.
implementsKeyword — How you apply an Interface to a class (similar to how youextendsan abstract class).- Classes Overview — Related concept: Classes Overview.
7. Key Takeaways
- Abstract Classes are base classes that cannot be instantiated directly (
new AbstractClass()is illegal). - They exist solely to be extended by other classes.
- They allow you to share concrete logic (standard methods) with subclasses.
- They allow you to define Abstract Methods (methods without bodies), which forces the subclass to implement their own version of that method.
- Prefer
interfaceif you do not need to share concrete logic.