[Typescript] Classes and Interfaces

찐새·2022년 7월 25일
0

typescript

목록 보기
4/8
post-thumbnail

4. Classes and Interfaces

4-1. Class

  • 객체 지향 프로그래밍 가능
class Player {
  constructor(
    private firstName: string,
    private lastName: string,
    public nickName: string
  ) {}
}

const js = new Player("js", "ss", "찐새");

js.firstName; // Property 'firstName' is private and only accessible within class 'Player'.
  • this를 사용하지 않고, JAVAC# 등의 방식으로 class를 생성할 수 있다.
  • firstNameprivate이기 때문에 호출할 수 없다.

4-1-1. Abstract Class

  • 추상 클래스도 사용할 수 있다.
  • 추상 클래스는 오직 다른 곳에서만 상속받을 수 있고, 인스턴스(객체)를 생성할 수 없다.
abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    public nickName: string
  ) {}
}

const js = new User("js", "ss", "찐새"); // Cannot create an instance of an abstract class.
  • 메서드를 생성하여 상속받아 사용할 수 있다.
abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    private nickName: string
  ) {}
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

class Player extends User {}

const js = new Player("js", "ss", "찐새");

console.log(js.getFullName()); // "js ss"
  • 추상 메서드 역시 생성 가능하지만, 구현해서는 안 되고, call signature만 가지고 있어야 한다.
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 js = new Player("js", "ss", "찐새");

js.getNickName(); // "찐새"
  • 위 코드에서는 추상 메서드 getNickNamevoid를 리턴한다는 call signature만 가지고 있다. 추상 메서드의 구현은 상속 받은 자식 클래스에서 이뤄졌다.

접근 한정자
constructor 프로퍼티 앞에 붙은 데이터 공개 여부를 '접근 한정자'라고 부른다.
public : 자식 클래스는 물론 외부에서도 접근 가능
protected : 자식 클래스는 접근 가능, 외부는 접근 불가능
private : 자식 클래스, 외부 모두 접근 불가능
접근 한정자를 public으로 두고 수정 불가능으로 만들고 싶다면, readonly를 붙여준다.
ex) public readonly firstName: string

  • [key: string]: string으로 type을 지정하면, string만을 가지는 object가 된다.
  • 아규먼트가 클래스 객체여야 한다면, call signature로 클래스를 지정할 수 있다.
type Words = {
  // string 만을 가지는 object
  [key: string]: string;
};

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

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

const kimchi = new Word("Kimchi", "Food");

const dict = new Dict();

dict.add(kimchi);
dict.def("Kimchi");

console.log(dict);
/* Dict: {
  "words": {
    "Kimchi": "Food"
  }
} */

4-2. Interfaces

  • 데이터 타입이 아닌 특정 값을 타입으로 지정하면, 그 외의 값은 허용하지 않게 된다.
type Team = "red" | "blue" | "yellow";
type Health = 1 | 5 | 10;

type Player = {
  nickname: string;
  team: Team;
  health: Health;
};

const js: Player = {
  nickname: "js", //모든 string타입 가능
  team: "blue", // "red", "blue", "yellow만 가능
  health: 10, // 1, 5, 10만 가능
};

const ts: Player = {
  nickname: "js",
  team: "pink", // Type '"pink"' is not assignable to type 'Team'.
  health: 2, // Type '2' is not assignable to type 'Health'.
};
  • interfacetype과 비슷한 역할을 한다.
type Player = {
  nickname: string;
  team: Team;
  health: Health;
};

interface Player {
  nickname: string;
  team: Team;
  health: Health;
}
  • type은 다양한 목적으로 사용 가능하다.
  • 반면, interface는 오직 오브젝트 모양을 타입스크립트에게 설명해주기 위해서만 사용되는 키워드이다.
  • 다른 interface를 상속하여 확장할 수 있다.
interface User {
  name: string;
}

interface Player extends User {}

const js: Player = {
  name: "js",
};
  • type을 사용하면 다음과 같다.
type User = {
  name: string;
};

type Player = User & {};

const js: Player = {
  name: "js",
};
  • 여러 개로 나뉜 interface는 타입스크립트가 알아서 하나로 합친다. type으로는 불가능하다.
// type
type User = {
  // Duplicate identifier 'User'.
  name: string;
};

type User = {
  // Duplicate identifier 'User'.
  nickname: string;
};

// interface
interface User {
  name: string;
}
interface User {
  nickname: string;
}
interface User {
  health: number;
}

const js: User = {
  name: "js",
  nickname: "JS",
  health: 10,
};

4-3. Class + Interface

  • 추상 클래스를 사용하여 클래스의 청사진을 제시할 수 있다.
abstract class User {
  constructor(protected firstName: string, protected lastName: string) {}
  abstract sayHi(name: string): string;
  abstract fullName(): string;
}

class Player extends User {
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
  sayHi(name: string): string {
    return `Hello ${name}. My name is ${this.fullName()}`;
  }
}

const js = new Player("java", "script");
console.log(js.fullName()); // "java script"
console.log(js.sayHi("ts")); // "Hello ts. My name is java script"
  • 단점은 javascript로 컴파일할 시 추상 클래스가 아닌 일반 클래스로 변경된다는 점이다.
  • 이 부분을 interface를 활용하여 더 가벼우면서 일반 클래스 컴파일을 방지할 수 있다.
interface User {
  firstName: string;
  lastName: string;
  sayHi(name: string): string;
  fullName(): string;
}

class Player implements User {
  constructor(public firstName: string, public lastName: string) {}
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
  sayHi(name: string): string {
    return `Hello ${name}. My name is ${this.fullName()}`;
  }
}
  • interface로 설정한 call signatures는 implements를 통해 클래스와 연결할 수 있다.
  • interface와 연결된 클래스는 해당 속성을 사용하지 않을 경우 타입스크립트가 경고하여 알려준다.
  • 단점은 접근 한정자가 public뿐이라는 점이다.
  • ,(콤마)를 이용해 여러 interface를 연결할 수 있다.
interface User {
  firstName: string;
  lastName: string;
  sayHi(name: string): string;
  fullName(): string;
}

interface Human {
  health: number;
}

class Player implements User, Human {
  constructor(
    public firstName: string,
    public lastName: string,
    public health: number
  ) {}
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
  sayHi(name: string): string {
    return `Hello ${name}. My name is ${this.fullName()}`;
  }
}

참고
Nomad coders - Typescript로 블록체인 만들기

profile
프론트엔드 개발자가 되고 싶다

0개의 댓글