본문 바로가기
Web/JavaScript

[TypeScript] Class

by llHoYall 2021. 7. 4.

The class of TypeScript is similar to other languages.

class Person {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
  
  introduce() {
    console.log(`My name is ${this.name}`);
  }
}

let person = new Person('hoya');
person.introduce();
// My name is hoya

Structural Type System

TypeScript uses structural type system.

That is, if the class has the same structure, they are compatible with each other.

class Person {
  name!: string;
}

class Animal {
  name!: string;
}

const instance: Person = new Animal();

What if the member of Person or Animal is a little different?

class Person {
  name!: string;
  age!: number;
}

class Animal {
  name!: string;
}

const instance: Person = new Animal();

There is an error that occurred.

Person has age property, but Animal doesn't.

Therefore requirements cannot meet.

class Person {
  name!: string;
}

class Animal {
  name!: string;
  age!: number;
}

const instance: Person = new Animal();

In the opposite case, Person's property can be filled with Animal's.

So, there is no error.

Method Overloading

Method overloading of TypeScript is the same as other OOP languages'.

It means that although the type and number of parameters or the type of return value are different, multiple methods with the same name can be created.

class MethodOverloading {
  method(): void; // declaration
  method(data: number): void; // declaration
  method(data?: number): void { // implementation
    if (typeof data === 'number') {
      console.log(`Data: ${data}`)
    } else {
      console.log(`No params`)
    }
  }
}

const instance = new MethodOverloading();

instance.method();  // No params
instance.method(7); // 7

The declaration code is not necessary. It is only for the automatic complete in IDE.

 

Let's look at another example.

interface IPerson {
  name: string
  age: number;
}

class MethodOverloading {
  method(name: string): IPerson; // declaration
  method(age: number): IPerson[]; // declaration
  method(data: string | number): IPerson | IPerson[] { // implementation
    if (typeof data === 'string') {
      return { name: data, age: 18 }
    } else if (typeof data === 'number') {
      return [{ name: 'HoYa', age: 18 }, { name: 'Park', age: 18 }]
    } else {
      return { name: 'Anonymous', age: -1 }
    }
  }
}

const instance = new MethodOverloading();

console.log(instance.method('Kim'));
// { "name": "Kim", "age": 18 } 
console.log(instance.method(17));
// [{ "name": "HoYa", "age": 18 }, { "name": "Park", "age": 18 }]

Like the above examples, method overloading in TypeScript can be implemented using optional or union.

In addition, it can be implemented using interface.

Inheritance

class Character {
  move(source: number = 0, target: number = 0) {
    console.log(`Moved from ${source} to ${target}`);
  }
}

class Enemy extends Character {
  attack() {
    console.log('Attack!');
  }
}

const orc = new Enemy();
orc.move(0, 3);
// Moved from 0 to 3
orc.attack();
// Attack!

Derived classes are often called subclasses, and base classes are often called superclasses.

When we extend the classes, we need to use extends keywords.

class Character {
  startPos: number;

  constructor(startPos: number) {
    this.startPos = startPos;
  }

  move(target: number = 0) {
    console.log(`Moved from ${this.startPos} to ${target}`);
  }
}

class Enemy extends Character {
  constructor(startPos: number) {
    super(startPos);
  }

  move(target: number = 3) {
    console.log("Enemy is moving...");
    super.move(target);
  }
}

class Player extends Character {
  move(target: number = 7) {
    console.log("Player is moving...");
    super.move(target);
  }
}

const orc = new Enemy(2);
const player = new Player(5);

orc.move(3);
// Enemy is moving... 
// Moved from 2 to 3 
player.move(3); 
// Player is moving... 
// Moved from 5 to 3

If you call methods in the superclass from the subclass, you have to use super keywords.

If you omit the constructor of the subclass, the constructor of the superclass is called automatically.

You can override methods of the superclass in the subclass.

Public, Private, and Protected Modifiers

In TypeScript, each member is public by default.

class Person {
  public name: string;
  protected gender: string;
  private address: string;
  #age: number;
  
  public constructor(name: string) {
    this.name = name;
  }
  
  public introduce() {
    console.log(`My name is ${this.name}`);
  }
}

let person = new Person('hoya');
person.introduce();
// My name is hoya
person.gender;  // Error: it is protected property and only accessible within class and its subclasses.
person.address;  // Error: it is private property and only accessible within class.
person.#age;  // Error: it is not accessible outside class because it has a private identifier.

With TypeScript 3.8, TypeScript supports the new JavaScript syntax for private fields #.

TypeScript also has its own way to declare a member as being marked private, it cannot be accessed from outside of its containing class.

Readonly Modifier

You can make properties read-only by using the readonly keyword.

class Person {
  readonly name: string;
  
  constructor(name: string) {
    this.name = name;
  }
}

let person = new Person('hoya');
person.name = 'kim';  // Error: it is a read-only property.

Parameter Properties

Parameter properties let you create and initialize a member in one place.

class Person {
  constructor(
    public name: string,
    protected gender: string,
    private address: string,
    readonly age: number
  ) { }
}

let person = new Person("hoya", "male", "seoul", 18);
console.log(person.name);
// hoya

Accessors

TypeScript supports getter/setters as a way of intercepting accesses to a member of an object.

class Person {
  private _name: string = "";
  
  get name(): string {
    return this._name;
  }
  
  set name(newName: string) {
    if (newName && newName.length > 8) {
      throw new Error('Too long (max. 8)');
    }
    this._name = newName;
  }
}

let person = new Person();
person.name = 'hoya';
console.log(person.name);
// hoya

Static Properties

We can create static members of a class, those that are visible on the class itself rather than on the instances.

class Point {
  static origin = { x: 0, y: 0 };
  
  constructor(public currentX: number, public currentY: number) { }
}

console.log(Point.origin);
// {x: 0, y: 0}

Abstract Class

Abstract classes are base classes from which other classes may be derived.

They may not be instantiated directly.

Unlike an interface, an abstract class may contain implementation details for its members.

Methods within an abstract class that are marked as abstract do not contain implementation and must be implemented in derived classes.

abstract class Shape {
  abstract getNumberOfPoint(): number;

  printMessage(): void {
    console.log('I am a Shape.');
  }
}

class Triangle extends Shape {
  getNumberOfPoint(): number {
    return 3;
  }
}

let shape = new Shape();  // Error: cannot create an instance of an abstract class.
let triangle = new Triangle();
console.log(triangle.getNumberOfPoint());
// 3
triangle.printMessage();
// I am a Shape.

'Web > JavaScript' 카테고리의 다른 글

[TypeScript] Generic  (0) 2021.07.05
[TypeScript] Singleton Pattern  (0) 2021.07.05
[TypeScript] Basic Types  (0) 2021.07.04
[Vue3] Getting Started with Vue3 + Electron (with Typescript, TailwindCSS)  (0) 2021.06.28
[Vue3] Vuex Usage  (0) 2021.06.26

댓글