TypeScript Abstract Classes
An abstract class is a class that cannot be created directly and can leave methods unfinished. Subclasses complete the unfinished methods and inherit the finished code.
A template method with one abstract step
The base render method always prints the same frame. Each subclass supplies only the body text.
abstract class ReportGenerator {
render(): string {
const head = "=== Report ===";
return head + "\n" + this.body() + "\n=== End ===";
}
abstract body(): string;
}
class SalesReport extends ReportGenerator {
body(): string {
return "Sales: 42 orders";
}
}
class StockReport extends ReportGenerator {
body(): string {
return "Stock: 17 items low";
}
}
console.log(new SalesReport().render());
console.log(new StockReport().render());Shared code reads subclass state
The base describe method is concrete and shared. The abstract channel method changes what the shared code prints.
abstract class Sender {
constructor(protected address: string) {}
abstract channel(): string;
describe(): string {
return `${this.channel()} -> ${this.address}`;
}
}
class EmailSender extends Sender {
channel(): string {
return "email";
}
}
class SmsSender extends Sender {
channel(): string {
return "sms";
}
}
console.log(new EmailSender("a@example.com").describe());
console.log(new SmsSender("+1-555-0100").describe());An abstract class cannot be instantiated
The base class alone has no body method, so creating it directly is refused. Only a completed subclass can be created.
abstract class ReportGenerator {
abstract body(): string;
render(): string {
return "Report: " + this.body();
}
}
const gen = new ReportGenerator();Example
abstract class ExportJob {
constructor(protected fileLabel: string) {}
abstract rows(): string[];
run(): string {
return `Export ${this.fileLabel}\n` + this.rows().join("\n");
}
}
class CsvExport extends ExportJob {
rows(): string[] {
return ["a,1", "b,2"];
}
}
class JsonExport extends ExportJob {
rows(): string[] {
return ['{"a":1}'];
}
}
console.log(new CsvExport("data.csv").run());
console.log(new JsonExport("data.json").run());Key points
- The `abstract` keyword marks a class or a method as unfinished.
- An abstract method has a signature and no body.
- A concrete subclass must implement every abstract method it inherits.
- Concrete methods in the base class run for every subclass unchanged.
- Creating an abstract class with `new` is refused by the checker.
- The base fixes the shared steps, and each subclass fills in the varying step.
In this example, the abstract ExportJob fixes the export header in its concrete run method. Each subclass supplies only its rows, and the shared header appears in both outputs.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท TypeScript Playground
Exercise
Why does `new ReportGenerator()` fail for an abstract class?