Questions for the typescript course that is on the way🙏🥳

Hi Mosh,

I saw that there is a Typescript course on the way! :partying_face:

I have some questions and I wondered if you can refer them on your upcoming course:

  1. When should I use Type and when should I prefer Class?
  2. Composition over Inheritance: How can I implement composition in Typescript?
    more specifically; does interfaces main purpose is to create composition?
  3. (If not for Question 2, then) Practical examples when should I use interface?

Is it possible to refer to these issues please?:pray::pray::pray:

1 Like

I am not an expert on TypeScript (just taking the course), but I believe the biggest difference between Type and Class is that Type does not allow you to have an implementation or default values (it just tells you the structure of the variable, nothing more). Also, classes can implement Interfaces (which I do not believe is possible with Types).

In practice, it looks like you should use a Type when you want a simple value class. For example:

type Point = {
  readonly x: number,
  readonly y: number,
}

Interfaces are perfect when you just want to declare the functions that a type should have:

interface Drawable {
  draw(): void
}

class Circle implements Drawable {
  ...
  draw(): void {
    // code that draws the circle
  }
}

Classes should be used whenever you need to share implementation (for example, you need all Circles to have the same draw() method as shown above).


As for the issue of Composition vs Inheritance, here is an example:

class Pushable {
  push(): void {
    // details omitted
  }
}

class InheritedPushable extends Pushable {
  // details omitted
}

class ComposedWithPushable {
  pushable: Pushable
  constructor(pushable: Pushable) {
    this.pushable = pushable;
  }
  // details omitted, but can use pushable.push() when needed
}

In general, you should only use inheritance when the two classes have an “is-a” relationship (in this case, InheritedPushable is a Pushable). Programmers are often tempted to treat something like this even though the classes do not have an “is-a” relationship leading to degenerate inheritance graphs.

When you just need behavior, you can compose the two classes so that the new class which needs the behavior has the other class inside. In our example, a ComposedWithPushable is not a Pushable but has one so that it can call the push() method when necessary.

A classic example where programmers are tempted to do the wrong thing is the relationship between a Rectangle and a Square. Mathematically, it seems to make sense that a Square is a special case of a Rectangle, but see this code for how this can go wrong:

class Rectangle {
  constructor(private length: number, private width: number) {}

  area(): number {
    return this.length * this.width;
  }

  setWidth(width: number): void {
    this.width = width;
  }
  // other details omitted
}

class Square extends Rectangle {
  constructor(side: number) {
    super(side, side);
  }
}

Did you see how Square inherited the setWidth class which allows us to have a square which has sides of different lengths! Instead, we should use composition where a Square has a Rectangle and delegates to that rectangle for the area():

class Square {
  rectangle: Rectangle
  constructor(side: number) {
    this.rectangle = new Rectangle(side, side);
  }
  area(): number {
    return this.rectangle.area();
  }
}