TypeScript Tutorial
TypeScript Abstract Classes
An abstract class cannot be constructed. You inherit it and implement the missing methods.
A class that is only a contract plus shared code
Mark a class abstract when it is not a complete object. tsc will not let you writenew Shape(). You extend it and fill in the methods markedabstract. Shared fields and concrete helpers can still live on the base.
C++ uses a pure virtual function: virtual string kind() = 0;. TypeScript writesabstract kind(): string; on an abstract class. The idea is the same: every concrete shape can answer kind(), but there is no generic shape sitting in memory with a fake answer.
abstract kind(): string
Write the name and the signature on the base. Do not write a body. In each derived class, writekind(): string and return a label. The derived class is concrete once every abstract method has an implementation.
const s = new Shape(); does not compile. const c = new Circle(); does, becauseCircle provides kind(). The polymorphism chapter used a base that still had a body. Here the base method has none.
Two derived types, one Shape parameter
You can still use the abstract type as a parameter type. A function that takes Shape will accept aCircle or a Square. The call to kind() runs the derived version.
Example
abstract class Shape {
abstract kind(): string;
}
class Circle extends Shape {
kind(): string {
return "circle";
}
}
class Square extends Shape {
kind(): string {
return "square";
}
}
function show(s: Shape): void {
console.log(s.kind());
}
const c = new Circle();
const q = new Square();
show(c);
show(q);Output is circle then square. show never names Circle orSquare. Uncommenting new Shape() inside this file is a compile error.
Open this in /typescript/try. Add a third derived class with its own kind(), pass it to show, and run again. The helper does not change.
Call kind on the objects directly
Direct calls work too. You do not need a helper. The parameter version matters when one function must handle every kind of shape without a growing if chain.
Example
abstract class Shape {
abstract kind(): string;
}
class Circle extends Shape {
kind(): string {
return "circle";
}
}
class Square extends Shape {
kind(): string {
return "square";
}
}
const c = new Circle();
const q = new Square();
console.log(c.kind());
console.log(q.kind());Shared concrete code on the base
Abstract classes exist because you may still want a method with a body. This base stores a label prefix and implements tag(). Each derived class only implements kind().
Example
abstract class Shape {
constructor(private prefix: string) {}
abstract kind(): string;
tag(): string {
return this.prefix + ":" + this.kind();
}
}
class Circle extends Shape {
constructor() {
super("shape");
}
kind(): string {
return "circle";
}
}
const c = new Circle();
console.log(c.tag());Output is shape:circle. super("shape") runs the base constructor. An interface (next chapter) cannot hold that constructor or the shared tag implementation unless you add a separate helper.
Rules that keep this small
- Mark the class
abstract. Mark incomplete methodsabstracttoo. - Each concrete class
extendsthe base and writes those methods. - Do not construct the abstract type. Construct
CircleorSquare. - Pass
Shapeas a parameter type when one function should accept every derived object. - If the base can provide a reasonable default, a plain method is enough — that was polymorphism. Abstract is the stricter tool.
When a class should be abstract
Use an abstract base when every derived type must supply a behavior and a default in the base would be a lie. A shape that cannot say what it is should not exist. Shared fields and non-abstract helpers can still live in the base; only the required hook is abstract. Next: interfaces, a contract that classes implement and objects can satisfy without a class.