// TypeScript: Cannot create an instance of an abstract class
// Bug: Base class without concrete implementation is used directly
class Repository {
tableName;
constructor(tableName) {
this.tableName = tableName;
}
// Should be abstract — no implementation in base class
findAll() {
// Returns undefined — no implementation
}
findById(id) {
// Returns undefined — no implementation
}
}
// Bug: instantiated directly instead of through a concrete subclass
const repo = new Repository('users');
const all = repo.findAll();
console.log(all); // undefined — base class has no data
console.log(typeof all);class Repository {
tableName;
// In TypeScript, mark as abstract class — cannot be instantiated
constructor(tableName) {
this.tableName = tableName;
}
// Abstract methods — must be implemented by subclasses
findAll() { return []; } // base fallback for JS demo
findById(id) { return null; }
}
// Concrete subclass — provides real implementation
class UserRepository extends Repository {
#data;
constructor() {
super('users');
this.#data = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
}
findAll() {
return this.#data;
}
findById(id) {
return this.#data.find(u => u.id === id) || null;
}
}
const repo = new UserRepository();
const all = repo.findAll();
console.log(all.length);
console.log(all[0].name);Bug: Repository should be abstract. Instantiating it directly gives a useless object where all methods return undefined. TypeScript's abstract class prevents this: "Cannot create an instance of an abstract class."
Explanation: UserRepository extends the base and provides concrete implementations. findAll() returns the actual data. TypeScript abstract classes prevent instantiating the base and force subclasses to implement abstract methods.
Key Insight: Abstract classes define contracts + shared behavior. Mark methods abstract when they must be overridden, and concrete when they provide a default implementation. TypeScript enforces at compile time that abstract methods are implemented.