C.W.K.
Stream
Lesson 03 of 06 · published

Abstract Classes and Methods

~8 min · classes, abstract, inheritance

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"Abstract is a class that says 'you must extend me to use me.'"

What abstract does

abstract class Base { ... } declares a class that can't be instantiated directly. new Base() is a compile error — you have to extend it first. Abstract classes are templates: they describe shared structure and partial implementation, leaving specifics for subclasses.

An abstract class can declare abstract method(): T — a method signature with no body. Concrete subclasses must implement it. This is the type-system version of 'these are the methods you must provide,' enforced at compile time.

When to reach for abstract

Abstract classes earn their keep when:

  • You have shared state and behavior across a family of types.
  • Subclasses must implement specific methods but reuse most of the rest.
  • You want to forbid direct instantiation of the base.

If you only need a contract (no shared state, no shared implementation), use an interface — it's lighter. Abstract classes are for when interface alone isn't enough.

Abstract vs interface

Both describe contracts. Abstract classes also provide implementation. The rule of thumb: if you need to share code, use abstract. If you only need to share the shape, use interface. Many codebases use both — an interface describes the contract, and an abstract class implements common behavior for that contract.

Abstract is the line between 'this is a contract' and 'this is a partially-built class.' Use it sparingly — most contracts in TypeScript live in interfaces. Reach for abstract when the contract also includes shared implementation.

Code

Abstract class with mixed concrete and abstract members·typescript
abstract class Shape {
  // Concrete shared field.
  color: string;

  constructor(color: string) {
    this.color = color;
  }

  // Concrete shared method.
  describe(): string {
    return `A ${this.color} ${this.kind}`;
  }

  // Abstract — subclasses must implement.
  abstract get kind(): string;
  abstract area(): number;
}

// new Shape('red')  // ❌ Cannot create an instance of an abstract class

class Circle extends Shape {
  radius: number;

  constructor(color: string, radius: number) {
    super(color);
    this.radius = radius;
  }

  get kind(): string { return 'circle' }
  area(): number { return Math.PI * this.radius ** 2 }
}

const c = new Circle('red', 10);
c.describe();     // 'A red circle'
c.area();         // ~314.15...

External links

Exercise

Write an abstract class Stream<T> with a concrete read(): T method that calls an abstract _pull(): T. Implement two concrete subclasses: StaticStream (returns a fixed value) and RandomStream (returns random). Confirm you can't new Stream().
Hint
The base provides the read-and-do-extra logic; subclasses provide the source of values. The pattern is common in stream libraries — read() is the template, _pull() is the variation point.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.