[TypeScript] CLASSES AND INTERFACES #4.0 - #4.1

uxolrv·2022년 12월 17일
1

nomadCoder - TypeScript

목록 보기
6/9
post-thumbnail

📌 How to create a class in TypeScript

class Player {
  constructor(
    private firstName: string,
    private lastName: string,
    public nickname: string
  ) {}
}

const nico = new Player("nico", "las", "니꼬")

TypeScript에서 class를 생성할 때에는 JavaScript와 달리, this를 사용하여 parameter를 할당하지 않고, parameter만 작성하여도 생성이 가능하다.

또한, 클래스 기반 객체 지향 언어가 지원하는 접근제한자를 사용하여 class를 생성할 수 있다.


위처럼 private property를 사용하여도 JavaScript로 컴파일된 후에는 사라지게 된다.

⇒ TypeScript에서 사용하는 접근제한자는 오로지 TypeScript에서 사용자를 보호하기 위한 장치!



nico.firstName // Err: Property 'firstName' is private and only accessible within class 'Player'

firstNameprivate 속성을 갖고 있기 때문에 접근이 불가능하게 된다.
⇒ 컴파일 에러 발생!








💡 접근제한자

public

어디서든지 접근할 수 있으며 외부 인터페이스를 구성(기본값)
→ 인스턴스에서 사용 가능

private

해당 클래스 내에서만 접근 가능 (자식 클래스에서도 접근 불가)
→ 인스턴스에서 사용 불가

protected

해당 클래스와 자식 클래스에서 접근 가능
→ 인스턴스에서 사용 불가


🔎 접근 가능 위치로 구분한 접근제한자

선언한 클래스 내상속받은 클래스 내인스턴스
private
protected
public








💡 추상 클래스 (Abstract Class)

다른 클래스가 상속받을 수 있는 클래스
그러나 추상 클래스는 직접 새로운 인스턴스를 만들 수는 없다.

abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    public nickname: string
  ) {}
}

// Player가 User를 상속받음
class Player extends User {
}

const nico = new Player("nico", "las", "니꼬")



🔎 추상 클래스를 상속받는 클래스 만들기

class (클래스 이름) extends (상속받을 추상클래스 이름) {}



🔎 추상 클래스의 인스턴스를 만들고자 하면?

const nico = new User("nico", "las", "니꼬")
// Err: Cannot create an instance of an abstract class

추상 클래스는 오직 다른 곳에서 상속받을 수만 있는 클래스로, 직접적으로 인스턴스를 만들 수는 없다!








💡 일반적인 class 내부 메소드

🔎 class 내부 메소드 만들기

abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    public nickname: string
  ) {}
  getFullName(){
    return `${this.firstName} ${this.lastName}`
  }
}

class Player extends User {
}

const nico = new Player("nico", "las", "니꼬")

nico.getFullName()

PlayerUser를 상속받았으므로, Player의 인스턴스인 nico에서 getFullName 메서드를 바로 사용할 수 있다.



abstract class User {
  constructor(
    ...
  ) {}
  private getFullName(){
    return `${this.firstName} ${this.lastName}`
  }
}

nico.getFullName() // Err

이때, getFullName 메소드를 private으로 만들게 되면, 더 이상 인스턴스에서의 접근이 불가능해진다.



🔎 JavaScript로 컴파일된 코드 살펴보기

JavaScript에서는 추상클래스를 만들 수 없으므로, abstract를 사용하여 만든 클래스가 일반 클래스로 컴파일되는 것을 확인할 수 있다.


또한, getFullNameprivate인데도 에러없이 작동이 되는 모습을 확인할 수 있다.

⇒ TypeScript에서 사용하는 접근제한자는 오로지 TypeScript에서 사용자를 보호하기 위한 장치!








💡 추상 메소드 (Abstract method)

❗️ 추상 메소드는 클래스 안에서 구현하지 않아야 하며, 메소드의 call signature만을 작성해야 한다.
또한, 추상 클래스를 상속하는 클래스는 추상 클래스에 정의된 메서드를 반드시 구현해야 한다.


abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    private nickname: string
  ) {}
  abstract getNickname(): void // call signature
}

// Err: Non-abstract class 'Player' does not implement inherited abstract member 'getNickName' from class 'User'
class Player extends User {
}

추상 메소드는 call signature로 이루어진 정의만 있을 뿐, body가 구현되어 있지 않다. (만일 메소드가 argument를 받는 경우, argument의 이름과 타입도 필요!)

추상 클래스를 상속하는 클래스에서 해당 추상 메소드의 body를 필히 구현해야 한다.

⇒ 현재 추상클래스 UsergetNickname이라는 추상메소드를 가지고 있으므로, User를 상속받는 Player에서 getNickname를 구현해주어야 한다.




🔎 protected 접근제한자 사용하기

abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    private nickname: string
  ) {}
  abstract getNickname(): void
}

class Player extends User {
  getNickname() {
    console.log(this.nickname) // Err!
  }
}

현재 nicknameprivate이기 때문에 this.nickname으로 접근이 불가능하여 getNickname 메서드를 구현할 수 없다.

⇒ property를 private으로 만든다면, 그 클래스를 상속했다할지라도 해당 property에 접근할 수 없다!



abstract class User {
  constructor(
    protected firstName: string,
    protected lastName: string,
    protected nickname: string
  ) {}
  abstract getNickname(): void
}

class Player extends User {
  getNickname() {
    console.log(this.nickname)
  }
}

const nico = new Player("nico", "las", "니꼬")

nico.firstName() // Err! 인스턴스에서는 protected 속성에 접근 불가

외부로부터 보호받으면서, 다른 자식 클래스에서는 사용하길 원한다면 protected를 사용하면 된다.








💡 실전 연습

간단한 사전 만들기!


type Words = {
    [key: string]: string // key와 value가 모두 string
}

위 문법은 해당 object의 key와 value가 모두 string이어야 함을 의미한다.

⇒ property의 정확한 개수와 이름은 모르지만, 타입은 알고 있을 경우 사용!



class Dictionary {
    private words: Words // property가 constructor로부터 바로 초기화되지 않도록 함
    constructor() {
        this.words = {} // 수동으로 초기화
    }
}

Constructor(생성자) 는 클래스의 인스턴스 객체를 생성하고 초기화하는 메서드이다.

constructor의 parameter로 words를 넣지 않음으로써, constructor로부터 words가 바로 초기화되지 않게 하고, 수동으로 초기화를 해주고 있다.



class Dictionary {
    private words: Words
    constructor() {
        this.words = {}
    }
    // class인 Word를 타입으로 사용!
    add(word: Word){
        // 아직 사전에 정의되지 않았다면
        if (this.words[word.term] === undefined) {
          // word의 term을 key로, word의 definition을 value로!  
          this.words[word.term] = word.definition; 
        }
    }
}

class Word {
    constructor(
        public term: string,
        public definition: string
    ) {}
}

메소드 add의 parameter 타입을 class인 Word로 정의하였다.

⇒ 이처럼 parameter가 특정 class의 인스턴스이길 원한다면, class를 타입으로 사용하는 것이 가능하다!



✨ 최종

type Words = {
    [key: string]: string
}

class Dictionary {
    private words: Words
    constructor() {
        this.words = {}
    }
    add(word: Word){
        if (this.words[word.term] === undefined) {
            this.words[word.term] = word.definition;
        }
    }
    get(term: string) {
        return this.words[term]
    }
}

class Word {
    constructor(
        public term: string,
        public definition: string
    ) {}
}

const kimchi = new Word("kimchi", "한국의 음식");
const dictionary = new Dictionary();

dictionary.add(kimchi)
dictionary.get("kimchi")


/*
Dictionary: {
  "words": {
    "kimchi": "한국의 음식"
  }
} 
*/








📌 정리

1️⃣ TS에서 class를 생성할 때에는 해당 필드의 접근 제어자, 이름, 타입만 작성하면 된다.
⇒ 기존 JS에서 행하던 this.firstName = firstName와 같은 번거로운 작업 생략 가능!


2️⃣ TS에서 public, protected, private과 같은 접근 제어자를 사용하여도 JS로 컴파일된 후에는 보이지 않는다.
⇒ TS의 보호장치!

선언한 클래스 내상속받은 클래스 내인스턴스
private
protected
public

3️⃣ TS에서는 추상 클래스를 만들 수 있으며, 이는 인스턴스를 직접적으로 만들 수 없는 클래스이다.
⇒ 대신, 추상 클래스를 상속한 클래스 생성 가능!


4️⃣ 추상 클래스에서 메소드를 구현한다면 상속받는 클래스에서 호출이 가능하다. 또한 메소드에도 접근 제어자 사용이 가능하다.


5️⃣ 추상 메소드는 추상 클래스 내부에서 call signature와 함께 정의만 되어있는 메소드이며, 추상 클래스를 상속받는 클래스에서 해당 메소드를 구현해야한다.


6️⃣ parameter가 특정 class의 인스턴스이길 원한다면, class를 타입으로 사용하는 것이 가능하다.








참고

https://ko.javascript.info/class

https://yamoo9.gitbook.io/typescript/classes/abstract-class








profile
안녕하세연🙋 프론트엔드 개발자입니다

0개의 댓글