티스토리 뷰

클래스

클래스 선언

private과 public을 사용해서 클래스 프로퍼티를 선언할 수 있다. 두개 다 자바스크립트에서는 보이지않고 문제없이 작동하지만 private은 클래스 밖에서 수정할 수 없고 public은 가능하다. 생략해서 적으면 public이 된다.

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

const kim = new Player("code", "kim", 'code kim')

kim.lastName // 에러메세지를 띄움
kim.nickname // 문제 없음

 

추상 클래스

abstract 키워드로 만들 수 있다. 다른 클래스가 상속 받을 수 있는 클래스이다. 추상 클래스가 인스턴스를 직접 생성하는 것은 불가능하다.

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

class Player extends User {
}

const kim = new User("code", "kim", 'code kim') // 에러메세지를 띄움
const lee = new Player("code", "lee", 'code lee') // 문제 없음

 

추상 메소드

추상 메소드도 abstract 키워드로 만들 수 있다. abstract를 쓰고 콜시그니처를 작성해주면 된다. 코드는 구현하지 않아도 된다. 추상 메소드를 만들면 추상 클래스를 상속받는 클래스에서는 필수로 추상 메소드를 구현해야한다.

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

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  abstract getNickname(): void; // 추상메소드
}
abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    private nickname: string
  ) {}

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  abstract getNickname(): void;
}

class Player extends User { // getNickname 추상메소드를 구현하지 않아서 에러메세지를 띄움
  
}

 

private으로 프로퍼티를 선언했다면 상속받은 class에서도 private에는 접근할 수 없다.

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

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  abstract getNickname(): void;
}

class Player extends User {
  getNickname() {
    console.log(this.nickname) // nickname에 접근할 수 없다고 에러메세지를 띄움
  }
}

 

접근하기는 원하고 값은 변경하지 못하게 하려면 protected를 쓰면 된다.

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

  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  abstract getNickname(): void;
}

class Player extends User {
  getNickname(): void {
    console.log(this.nickname) // 이젠 접근할 수 있음
  }
}

const lee = new Player('code', 'lee', 'code lee');

lee.nickname // 여전히 값을 수정하려고 하면 에러메세지를 띄움

 

클래스를 타입으로 쓸수도 있다.

type Words = {
  [key: string]: string; // 객체의 타입을 설정할 때 사용, 타입만 알 때 사용함
};

class Word {
  constructor(public term: string, public def: 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;
  }
}

const kimchi = new Word('kimchi', 'Korean Food');

const dict = new Dict();

dict.add(kimchi);
dict.def('kimchi'); // "Korean Food"

 

 

값을 읽을수만 있게 하고 수정은 못하게 하고 싶으면 readonly를 사용하면된다. 주로 다른 누군가가 데이터를 덮어씌우는 것을 방지하기 위해 사용한다.

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

const kimchi = new Word('kimchi', 'Korean Food');

kimchi.def = "Food" // readonly라서 에러메세지를 띄움

 

static을 사용하면 바닐라 자바스크립트 코드처럼 작동한다.

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;
  }

  static hello() { // static
    console.log("hello")
  }
}

 

인터페이스

객체의 타입을 정해주기 위한 방법은 두가지가 있다.

이렇게 타입을 정해서 넣어주는 방법과

type Nickname = string;
type Team = "red" | "blue" | "yellow" // 이렇게 값을 제한적으로 들어가게도 할수있음

type Player = {
  nickname:Nickname,
  team: Team
}

const bong : Player = {
  nickname : "bong",
  team : "red" // type Team에 있는 값을 넣었기 때문에 에러메세지를 띄우지 않음
}

const kim : Player = {
  nickname : "kim",
  team : "white" // type Team에 있는 값을 넣지 않았기 때문에 에러메세지를 띄움
}

 

인터페이스를 사용하는 방법이 있다. React에서 많이 사용하고 타입스크립트에만 있기 때문에 자바스크립트 코드로 바꾸면 보이지 않는다.

type Nickname = string;
type Team = "red" | "blue" | "yellow"

interface Player {
  nickname:Nickname,
  team: Team
}

const bong : Player = {
  nickname : "bong",
  team : "red"
}

 

인터페이스 키워드를 사용하면 타입 키워드처럼 다양하게 사용할 수 없다. 인터페이스는 오직 객체의 모양을 정해주기 위해서만 존재한다. 

type Name = string; // 사용 가능
interface Score = number; // 사용 불가능, 에러메세지를 띄움, =을 사용하지 않음

 

사용할 때는 형식이 똑같고 둘 중에 무엇을 사용해도 상관은 없다.

// type keyword
type User =  {
  name: string
}

type Player =  User & { // type이 상속을 이용하는 방법 &

}

const bong : Player = { // 똑같음
  name :"bong"
}
// interface keyword
interface User {
  name: string
}

interface Player extends User {

}

const bong : Player = { // 똑같음
  name :"bong"
}

 

인터페이스는 같은 이름으로 여러번 선언해도 타입스크립트에서 알아서 합쳐준다.

interface User {
  name: string
}

interface User {
  age: 10
}

const bong : User = {
  name :"bong",
  age : 20
}
// type keyword는 사용 불가
type User = { // 중복이라서 에러메세지를 띄움
  name: string
}

type User = { // 중복이라서 에러메세지를 띄움
  age: 10
}

 

인터페이스를 사용해야 할 때

추상 클래스를 사용했을 때 자바스크립트 코드로 컴파일하면 추상 클래스와 상속받은 클래스 모두가 나타난다. 추상 클래스를 사용하지 않는데도 불필요하게 추상 클래스가 존재한다. 그럴 때 타입을 사용할 수도 있지만 인터페이스를 사용하면 상속이 편리하다는 이점이 있다.

 

implements를 사용해서도 상속할 수 있다. 하지만 implements를 사용하면  private이나 protected는 사용할 수 없다.

interface User {
  firstName: string;
  lastName: string;
  sayHi(name: string): string;
  fullName(): string;
}

class Player implements User {
  constructor(
    public firstName: string, // public만 사용 가능
    public lastName: string
  ) {}
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
  sayHi(name: string) {
    return `Hi! ${name}. My name is ${fullName()}`;
  }
}

 

여러 개도 상속받을 수 있다. 인터페이스도 클래스처럼 타입으로 사용할 수 있다.

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) {
    return `Hi! ${name}. My name is ${fullName()}`;
  }
}

 

종합 활용 예제

로컬스토리지 API를 흉내낸 클래스. 타입별로 로컬스토리지를 생성할 수 있게 했다.

그동안 봤던 제네릭, 다형성, 클래스, 인터페이스 등을 이렇게 사용할 수 있다.

interface MyStorage<T> { // 인터페이스
  [key: string]: T;
}

class LocalStorage<T> { // 클래스, generic의 상속
  private Storage: MyStorage<T> = {};
  set(key: string, value: T) {
    this.Storage[key] = value;
  }
  remove(key: string) {
    delete this.Storage[key];
  }
  get(key: string): T {
    return this.Storage[key];
  }
  clear() {
    this.Storage = {};
  }
}

const stringStorage = new LocalStorage<String>(); // 다형성
stringStorage.get('x');
stringStorage.set("hello", "hi")

const booleanStorage = new LocalStorage<Boolean>();
booleanStorage.get('x');
booleanStorage.set("hello", true) // 설정한 것에 따라 다른 타입의 인수를 받음

 

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
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 30 31
글 보관함