Object-Oriented Programming in TypeScript

I replicated Object-Oriented Programming in Go to TypeScript. You can read the explanation or theory of OOP in that article. I'm here to show you how I technically replicated the code.

Abstraction

We created a class as a template to define an object. Of course, a class or object has properties and methods.

Properties

1
2
3
4
5
export default class Animal {
  name: string = '';
  feet: number = 0;
  hasPaws: boolean = false;
}

We created a class called Animal, exported the class, and set a default or initial value for its properties. This class is on a file named Animal.ts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import Animal from './Animal'

let cat: Animal = new Animal()

cat.name = 'Cat'
cat.feet = 4

let dog: Animal = new Animal()

dog.name = 'Dog'
dog.feet = 4
dog.hasPaws = true

console.log(cat)
console.log(dog)

We created a file named index.ts, imported the Animal class on it, defined two objects called cat and dog, and defined their properties.

Methods

1
2
3
4
5
6
7
print() {
  console.log(this.name, 'has', this.feet, 'feet.')

  if (this.hasPaws) {
    console.log(this.name, 'has paws.')
  }
}

We already have the properties. Let's create a method to print the properties. Add the print function to the Animal class.

1
2
cat.print()
dog.print()

In index.ts, we called the method.

Inheritance

1
2
3
4
5
import Animal from './Animal'

export default class Monster extends Animal {
  abilities: string[] = [];
}

Let's create a sub-class for the Animal that has abilities.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import Monster from './Monster'

let dragon: Monster = new Monster()

dragon.name = 'Dragon'
dragon.feet = 4
dragon.hasPaws = true
dragon.abilities = ['Nuclear blast', 'Ice smoke']

dragon.print()

The dragon has a print method from its parent.

Polymorphism

1
2
3
4
5
6
7
8
print() {
  console.log(this.name, 'is a monster!!!')
  console.log(this.name, 'has:')

  for (let ability of this.abilities) {
    console.log('-', ability)
  }
}

Let's create another form of the print method for the Monster class that prints abilities. Put the print method to the Monster class and execute the index.ts. We will see a different result.

Encapsulation

Encapsulation is about Access Modifiers. Let me explain about Interface and Constructor first.

Interface

1
2
3
4
5
6
7
8
export default interface Properties {
  set name(name: string);
  get name(): string;
  set feet(feet: number);
  get feet(): number;
  set hasPaws(hasPaws: boolean);
  get hasPaws(): boolean;
}

The interface allows us to define a must-defined method for classes that implements it. Here, we created getters and setters for name, feet, and hasPaws.

1
2
3
4
5
import Properties from './Properties'

export default class Animal implements Properties {
  ...
}

In the Animal.ts, we imported the interface of Properties and implemented it in the Animal class. Try to remove the property for an error.

Constructor

We created objects and defined their properties before. That is ugly. The constructor allows us to define properties along with the object.

1
2
3
4
5
constructor(name: string, feet: number, hasPaws: boolean) {
  this.name = name;
  this.feet = feet;
  this.hasPaws = hasPaws;
}

The constructor of the Animal.

1
2
3
4
constructor(name: string, feet: number, hasPaws: boolean, abilities: string[]) {
  super(name, feet, hasPaws);
  this.abilities = abilities;
}

The constructor of the Monster.

1
let dragon: Monster = new Monster('Dragon', 4, true, ['Nuclear blast', 'Ice smoke'])

No need to define properties anymore. We can create a monster in a single line.

Access Modifiers

There are three access modifiers in TypeScript; private, protected, and default (public). The private only allows to its class. The protected allows its sub-classes.

1
2
3
4
5
6
7
8
9
export default interface Properties {
  set name(name: string);
  get name(): string;
  set feet(feet: number);
  get feet(): number;
  set hasPaws(hasPaws: boolean);
  get hasPaws(): boolean;
  print(): void;
}

I put the print method into the interface of Properties to make it a must-defined method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import Properties from './Properties'

export default class Animal implements Properties {
  private _name: string = '';
  private _feet: number = 0;
  private _hasPaws: boolean = false;

  constructor(name: string, feet: number, hasPaws: boolean) {
    this.name = name;
    this.feet = feet;
    this.hasPaws = hasPaws;
  }

  set name(name: string) {
    this._name = name;
  }

  get name() {
    return this._name;
  }

  set feet(feet: number) {
    this._feet = feet;
  }

  get feet() {
    return this._feet;
  }

  set hasPaws(hasPaws: boolean) {
    this._hasPaws = hasPaws;
  }

  get hasPaws() {
    return this._hasPaws;
  }

  print() {
    console.log(this.name, 'has', this.feet, 'feet.')

    if (this.hasPaws) {
      console.log(this.name, 'has paws.')
    }
  }
}

I added a prefix of _ (underscore) to name, feet, and hasPaws to make them different from getters and setters. I also made them private.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Animal from './Animal'

export default class Monster extends Animal {
  private _abilities: string[] = [];

  constructor(name: string, feet: number, hasPaws: boolean, abilities: string[]) {
    super(name, feet, hasPaws);
    this.abilities = abilities;
  }

  protected set abilities(abilities: string[]) {
    this._abilities = abilities;
  }

  protected get abilities(): string[] {
    return this._abilities;
  }

  print() {
    console.log(this.name, 'is a monster!!!')
    console.log(this.name, 'has:')

    for (let ability of this.abilities) {
      console.log('-', ability)
    }
  }
}

Same thing for the Monster.

Closing

You can get the source code at https://github.com/aristorinjuang/ts-oop. Try to check out my references. They are good resources from official TypeScript. I didn't try static property or method, abstract class, decorator, overloads, or more. But I hope this article can give you a basic understanding of OOP in TypeScript.

References

Related Articles