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'
firstName
은 private
속성을 갖고 있기 때문에 접근이 불가능하게 된다.
⇒ 컴파일 에러 발생!
어디서든지 접근할 수 있으며 외부 인터페이스를 구성(기본값)
→ 인스턴스에서 사용 가능
해당 클래스 내에서만 접근 가능 (자식 클래스에서도 접근 불가)
→ 인스턴스에서 사용 불가
해당 클래스와 자식 클래스에서 접근 가능
→ 인스턴스에서 사용 불가
선언한 클래스 내 | 상속받은 클래스 내 | 인스턴스 | |
---|---|---|---|
private | ⭕ | ❌ | ❌ |
protected | ⭕ | ⭕ | ❌ |
public | ⭕ | ⭕ | ⭕ |
다른 클래스가 상속받을 수 있는 클래스
그러나 추상 클래스는 직접 새로운 인스턴스를 만들 수는 없다.
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
추상 클래스는 오직 다른 곳에서 상속받을 수만 있는 클래스로, 직접적으로 인스턴스를 만들 수는 없다!
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()
Player
가 User
를 상속받았으므로, Player
의 인스턴스인 nico
에서 getFullName
메서드를 바로 사용할 수 있다.
abstract class User {
constructor(
...
) {}
private getFullName(){
return `${this.firstName} ${this.lastName}`
}
}
nico.getFullName() // Err
이때, getFullName
메소드를 private
으로 만들게 되면, 더 이상 인스턴스에서의 접근이 불가능해진다.
JavaScript에서는 추상클래스를 만들 수 없으므로, abstract
를 사용하여 만든 클래스가 일반 클래스로 컴파일되는 것을 확인할 수 있다.
또한, getFullName
이 private
인데도 에러없이 작동이 되는 모습을 확인할 수 있다.
⇒ TypeScript에서 사용하는 접근제한자는 오로지 TypeScript에서 사용자를 보호하기 위한 장치!
❗️ 추상 메소드는 클래스 안에서 구현하지 않아야 하며, 메소드의 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를 필히 구현해야 한다.
⇒ 현재 추상클래스 User
가 getNickname
이라는 추상메소드를 가지고 있으므로, 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!
}
}
현재 nickname
이 private
이기 때문에 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