클래스에서의 타입스크립트
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`안녕하세요! 제 이름은 ${this.name}이고, 나이는 ${this.age}살입니다.`);
}
}
const person = new Person('Spartan', 30);
person.sayHello();
Person의 프로퍼티로 사용되는 name과 age이 constructor보다 상단에 위치하여 타입 지정이 되고 있다.
또한 Person이라는 클래스의 인스턴스를 생성할 때 필요한 name과 age 변수 즉 constructor에 정의된 변수의 타입도 지정되어 있다.
얼핏 보기에는 동일한 변수가 중복 타입 지정되는 것으로 보여질 수 있으나 class 최상단에 정의된 변수는 this.name과 this.age에 대한 것이며, constructor 내부에 정의된 변수는 인스턴스 생성 시 필요한 파라미터에 대한 타입 정의를 의미한다.
클래스 접근 제한자
타입스크립트의 강점 중 하나는 클래스 프로퍼티의 접근을 제한할 수 있다는 점이다.
public
- 클래스 외부에서도 접근이 가능한 접근 제한자입니다.
- 접근 제한자가 선언이 안되어있다면 기본적으로 접근 제한자는 public 입니다.
private
- 클래스 내부에서만 접근이 가능한 접근 제한자입니다.
- 보통은 클래스의 속성은 대부분 private으로 접근 제한자를 설정합니다.
- 클래스의 속성을 보거나 편집하고 싶다면 별도의 getter/setter 메서드를 마련합니다.
protected
- 클래스 내부와 해당 클래스를 상속받은 자식 클래스에서만 접근이 가능한 접근 제한자입니다.
class Person {
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
public sayHello() {
console.log(`안녕하세요! 제 이름은 ${this.name}이고, 나이는 ${this.age}살입니다.`);
}
}
upcasting, downcasting
부모 클래스의 상속을 받은 자식 클래스 이 둘의 관계에서 부모가 지닌 타입과 자식이 가진 타입을 각각 슈퍼 타입
과 서브 타입
으로 부른다.
이는 클래스 자체가 하나의 타입으로서 활용될 수 있기 떄문이다. 그런 의미에서 아래와 같이 쓰인다.
const person: Person = new Person('apple', 99);
Person은 클래스명이지만 타입을 정의하는 자리에 비치되어 있음을 알 수 있다. 사실 위 코드에서 타입 자리에 Person을 입력하지 않아도 오류가 발생하지 않는다. 왜냐하면 new Person을 통해 인스턴스를 생성하므로 이미 타입이 정의된 바와 같기 때문이다.
하지만 upcasing과 downcasting과 같은 경우에는 확실한 타입 지정을 필요로 한다.
먼저 upcasting 케이스를 살펴보자.
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`이름: ${this.name} 나이: ${this.age}살`);
}
}
class Admin extends Person {
role: string;
constructor(name: string, age: number) {
super(name, age);
this.role = 'admin';
}
sayRole = () => {
console.log(this.role);
};
}
const admin: Admin = new Admin('apple', 99);
admin.sayRole();
// admin이 Person 클래스
const person: Person = admin; // upcasting
// person.sayRole(); // 작동 불가
admin이라는 인스턴스가 Person 타입으로 재정의 되고 있다. admin은 super메서드를 통해 person의 상속을 받고 있었기 때문에 Person 타입이 입력받아야할 값을 그대로 지니고 있어 person 인스턴스 생성의 재료로 사용되는데 문제가 없다.
다만 이렇게 될 경우 person이 제아무리 admin을 그대로 받았다 할지라도 Admin 클래스에서 지니고 있던 sayRole 함수는 더이상 사용이 불가하다. 이제 admin이 부모 클래스를 기준으로 재정의 되었기 때문이다.
admin은 자신과 부모 클래스가 지닌 함수 모두에 접근이 가능한데 upcasting을 쓸 경우 자식 클래스의 함수 접근이 불가해지고, 이러한 의도가 필요한 곳에 쓰일 수 있겠다.
downcasting은 그 반대다. 부모가 자식 클래스를 타입으로 받아들이므로 부모가 지니지 못했던 자식 클래스의 함수에 접근이 가능해진다.
const person: Person = new Admin('apple', 99);
// person.sayRole() // 불가능
const adminPerson: Admin = person as Admin; // downcasting
adminPerson.sayRole(); // 가능
Admin 클래스로 인스턴스를 생성할 때 슈퍼 타입으로 생성되었다. 즉 Person 클래스의 인스턴스가 생성된 것이다. 그러니 당연히 자식 클래스에 존재하는 sayRole 함수에는 접근이 불가하다.
하지만 person 인스턴스의 타입을 일시적으로 서브 타입으로 변경하여 자식 클래스 인스턴스로 생성한다면(downcasting한다면) sayRole 함수에 접근이 가능하다.
이는 다소 이상한 그림이기는 하다. 왜냐하면 저렇게 할 바에 그냥 처음부터 자식 클래스인 Admin 클래스 인스턴스를 생성하는 것과 차이가 없기 때문이다.
자식 클래스는 부모 클래스의 상속을 받으므로 부모 클래스의 함수에도 접근이 가능하기 때문이다.
추상 클래스
자식 클래스에서 공통적으로 갖고 있는 함수(이름은 갖으나 기능이 다른)를 부모 클래스에서 일괄 처리해주기 위한 기능 구현에서 활용된다. 예시를 보자.
// 추상클래스와 추상함수
abstract class Shape {
abstract getTotal(): number;
printTotal() {
console.log(this.getTotal());
}
}
class Case1 extends Shape {
age: number;
constructor(age: number) {
super();
this.age = age;
}
getTotal() {
return this.age * 10;
}
}
class Case2 extends Shape {
name: string;
constructor(name: string) {
super();
this.name = name;
}
getTotal() {
return this.name.length * 10;
}
}
const case1 = new Case1(30);
const case2 = new Case2('apple');
case1.printTotal();
case2.printTotal();
Case1클래스와 Case2 클래스는 공통적으로 getTotal 함수
를 지니고 있다. 하지만 보다시피 작동 방식(기능)이 다르다.
만약 이 함수의 실행 결과를 프린트해주는 함수도 필요하다면 Case1과 Case2에 각각 동일한 형태의 프린트 함수를 생성해야 할 것이다.
하지만 이러한 코드 중복을 방지하는 방법으로 위 코드와 같은 추상 클래스를 도입한다. Case1과 Case2를 자식태그로 만들어 그 부모 태그인 Shape에서 getTotal함수를 추상 함수로 지정한다.
여기서 abstract
라는 키워드를 사용하며 추상 함수
로 등록된 getTotal을 지니고 있는 이상 Shape 클래스도 추상 클래스로의 지정이 필요하다.
이렇게하면 추상 클래스에서의 getTotal은 Shape 클래스 내에서 참조용으로서 표기되며 이를 기반으로 추가 기능 구현이 가능해 진다. 그 결과가 printTotal 함수이다.
이제 자식 클래스 인스턴스는 부모 클래스의 printTotal 함수
에 접근이 가능하게 되므로 불필요한 중복 코드 작성을 줄일 수 있게 되었다.
'백엔드 개발자(node.js)가 되는 과정' 카테고리의 다른 글
Nest.js 입문을 위한 맛보기 정리 (0) | 2023.08.03 |
---|---|
passport 패키지를 활용한 local 로그인 인증 구현 (0) | 2023.07.31 |
타입스크립트(typescript) 활용을 위한 기본 (0) | 2023.07.26 |
AWS의 주요 서비스 간략 정리 (1) (0) | 2023.07.25 |
[WIL] 23. 07. 18 ~ 23. 07. 23 프로젝트 회고 (0) | 2023.07.24 |