Typescript Class

전통적인 JavaScript는 재사용 가능한 컴포넌트를 빌드하는 기본 수단으로 함수와 프로토 타입 기반 상속에 중점을두고 있지만, 클래스가 기능을 상속하고 객체가 이러한 클래스에서 빌드되는 객체 지향 접근 방식에 익숙한 프로그래머에게는 다소 어색함을 느낄 수 있습니다. ECMAScript 2015(ECMAScript 6)에서 부터는 JavaScript 프로그래머는이 객체 지향 클래스 기반 접근 방식을 사용하여 응용 프로그램을 빌드 할 수 있습니다. TypeScript에서는 다음 JavaScript 버전을 기다릴 필요없이 개발자들이 이 기술들을 지금 사용하고 모든 주요 브라우저 및 플랫폼에서 작동하는 JavaScript로 컴파일 할 수 있습니다.

Class

1
2
3
4
5
6
7
8
9
10
11
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");

이전에 C #이나 Java를 사용했다면 문법이 익숙할 것입니다. 새로운 클래스인 Greeter를 선언합니다. 이 클래스에는 greeting 속성, 생성자 및 greet method의 세 가지 멤버가 있습니다.

클래스의 멤버를 참조할때 this.를 앞에 붙였습니다.

마지막 줄에서는 new를 사용하여 Greeter 클래스의 인스턴스를 생성합니다. 앞서 정의한 생성자를 호출하여 Greeter 형태의 새 객체를 만들고 생성자를 실행하여 초기화합니다.

Inheritance

TypeScript에서는 일반적인 객체 지향 패턴을 사용할 수 있습니다. 물론 클래스 기반 프로그래밍에서 가장 기본적인 패턴 중 하나는 상속을 사용하여 기존 클래스를 확장하여 새로운 클래스를 생성 할 수 있다는 것입니다.

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
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);

이 예제는 TypeScript의 다른 언어에 공통적 인 상속 기능 중 상당수를 다룹니다. 여기서 하위 클래스를 만드는 데 사용되는 extends 키워드를 볼 수 있습니다.

생성자 함수를 포함하는 파생 클래스는 기본 클래스의 생성자 함수를 실행하려면 super()를 호출해야합니다.

또한이 예제는 기본 클래스의 메서드를 하위 클래스에 특화된 메서드로 재정의하는 방법을 보여줍니다. 여기에서 Snake와 Horse는 Animal로부터의 move를 오버라이드하는 move 메소드를 생성하여 각 클래스에 특정 기능을 부여합니다. tomAnimal로 선언되었지만 그 값은 Horse이므로 tom.move(34)에서 Horse에서 재정의 메소드를 호출합니다.

1
2
3
4
Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.

Public, Private and Protected modifier

public by default

TypeScript에서는 class의 각 멤버는 기본적으로 public입니다.

private

private로 지정하면 class밖에서 접근할 수 없습니다.

1
2
3
4
5
6
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // 에러

TypeScript는 structural type 시스템입니다. 두 가지 type을 비교할 때, 모든 멤버의 type이 호환 가능하다면 type 자체가 호환 가능하다고 말합니다.

그러나 privateprotected 멤버가 있는 type을 비교할 때 이러한 유형을 다르게 처리합니다. 두개의 type이 호환 가능한 것으로 간주되려면 그 중 하나에 private 멤버가 있는 경우 다른 하나도 동일한 선언에서 시작된 private 구성원을 가져야합니다. protected 멤버도 마찬가지입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
}
class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
animal = employee; // 에러: 'Animal'과 'Employee'는 호환되지 않습니다

이 예제에서는 AnimalRhino가 있고 RhinoAnimal의 하위 클래스있습니다. 또한 형태면에서 Animal과 똑같이 보이는 새로운 클래스 Employee가 있습니다. 우리는 이러한 클래스의 인스턴스를 생성 한 다음 서로를 할당하여 어떤 일이 발생하는지 봅니다. Animalprivate name: string을 동일한 선언에서 형태의 private 측면을 공유하기 때문에 AnimalRhino는 호환됩니다. 그러나 Employee의 경우는 그렇지 않습니다. Employee에서 Animal로 할당하려고 할 때 이러한 type이 호환되지 않는다는 오류가 발생합니다. Employee에는 name이라는 private 멤버가 있지만, Animal에서 선언 한 멤버는 아닙니다.

protected

protected 멤버는 하위클래스에서 접근할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 에러

constructor를 protectec로 지정할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // 에러: 'Person' constructor is protected

Readonly modifier

readonly 키워드를 사용하여 속성을 읽기 전용으로 만들 수 있습니다. 읽기 전용 속성은 선언 또는 생성자에서 초기화해야합니다.

1
2
3
4
5
6
7
8
9
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error! name is readonly.

parameter property

마지막 예제에서 우리는 Octopus 클래스에서 읽기 전용 멤버 name과 생성자 매개 변수 인 theName을 선언해야만했습니다. 그런 다음 name을 theName으로 즉시 설정했습니다. 이것은 매우 일반적인 방법으로 밝혀졌습니다. 매개 변수 속성을 사용하면 한 곳에서 멤버를 만들고 초기화 할 수 있습니다. 다음은 매개 변수 속성을 사용하여 이전 Octopus 클래스의 추가 개정판입니다.

1
2
3
4
5
class Octopus {
readonly numberOfLegs: number = 8;
constructor(readonly name: string) {
}
}

parameter property는 accessibility modifier나 readonly를 생성자 매개 변수 앞에 접두어를 붙임으로써 선언됩니다.

static property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

abstrac class

abstract 클래스는 다른 클래스를 파생시킬 수있는 기본 클래스입니다. 직접 인스턴스화 할 수 없습니다. 인터페이스와 달리 abstract 클래스에는 멤버에 대한 구현 세부 정보가 포함될 수 있습니다. abstract 키워드는 abstract 클래스뿐만 아니라 abstract 클래스 내에서 abstract 메소드를 정의하는 데 사용됩니다.

1
2
3
4
5
6
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("roaming the earth...");
}
}

abstract으로 표시된 abstract 클래스 내의 메소드에는 구현이 포함되어 있지 않으므로 파생 클래스에서 구현해야합니다. abstract 메소드는 인터페이스 메소드와 유사한 구문을 사용합니다. 둘 다 메소드 본문을 포함하지 않고 메소드의 서명을 정의합니다. 그러나 abstract 메소드는 abstract 키워드를 포함해야하며 선택적으로 access modifier를 포함 할 수 있습니다.

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
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log("Department name: " + this.name);
}
abstract printMeeting(): void; // must be implemented in derived classes
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing"); // constructors in derived classes must call super()
}
printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
generateReports(): void {
console.log("Generating accounting reports...");
}
}
let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: method doesn't exist on declared abstract type
Share