[TypeScript] interface, type, class

summereuna🐥·2023년 8월 30일
0

TypeScript

목록 보기
9/13

public이지만 더 이상 변경할 수 없도록 하려면 프로퍼티를 readonly로 만들면 된다.
그러면 값은 볼 수 있지만 수정은 불가능해진다.

//단어 클래스
class Word {
   constructor(
      public readonly term: string, //용어
      public readonly definition: string, //정의
   ){}
}

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

kimchi.definition = "아아아아" //readonly이기 때문에 정의 수정 불가능

type


1. type을 사용하여 타입스크립트에게 오브젝트{}의 모양을 알려줄 수 있다.

type Player = {
  nickname: string,
  hp: number,
}

//type을 사용하여 타입스크립트에게 오브젝트의 모양 알려줄 수 있다.
const rm : Plyaer = {
  nickname: "RM",
  hp: 100,
}

2. type을 사용하여 그 타입의 타입유형을 선언할 수 있다.

type Food = string;

const kimchi: Food = "김치"

3. type alias(별칭, 대체명)을 사용할 수 있다.

type Nickname = string
type Hp = number
type Friends = Array<string>

type Player = {
  nickname: Nickname,
  hp: Hp,
  friends: Friends
}

const rm: Player = {
  nickname: "RM",
  hp: 100,
  friends: ["j-hope", "jin"]
}

4. 타입을 지정된 옵션으로만 제한(constrain)할 수도 있음

//일반적인 string이 아닌 특정 string이 되게 하기
type Team = "red" | "blue" | "yellow"
//이런식으로 concrete 타입의 특정 값을 쓸 수도 있다.
//그러면 팀은 red, blue, yellow, 세가지 옵션 중 하나만 가능!
//다른 문자열 넣으면 다 오류뜸

type Hp = 1 | 5 | 10
//이러면 에이치피 값이 1,5,10 중에 하나만 가능

type Player = {
  nickname: string,
  team: Team,
  hp: Hp
}

const rm: Player = {
  nickname: "RM",
  team: "yellow",
  hp: 10
}

이처럼 type 키워드를 사용하여 타입스크립트에서 만들고 싶은 무수히 많은 종류의 타입을 설명하면 된다.


interface

오브젝트의 모양(shape of object)을 설명하는 다른 방법인 인터페이스

인터페이스는 타입과 두 가지 차이점은 있지만 거의 비슷하다.

//type
type Player = {
  nickname: string,
  team: string,
  hp: number
}

//interface
interface Member {
  nickname: string,
  team: string,
  hp: number
}

const rm: Member = {
  nickname: "RM",
  team: "yellow",
  hp: 10
}

인터페이스는 오직 한가지 용도! 오브젝트의 모양을 설명하기 위해 사용한다.

  • 리액트 이용해서 작업할 때 특히 많이 사용된다.

따라서 TS에게 오브젝트의 모양을 알려주는 방법은 2가지이다.
1. type
2. interface

type 키워드가 interface 키워드에 비해 좀더 활용 할 수 있는 것이 많다.

근데 interface를 다루는게 클래스를 다루는 듯한 느낌이라 좀 더 쉽다.

interface User {
  name: string
}

//User 인터페이스 상속받은 Player
interface Player extends User {
}

const rm: Player = {
  name: "RM"
}

이처럼 인터페이스는 클래스랑 좀 닮았다.

타입으로는 이렇게 작성해야 한다.

type User = {
  name: string
}

type Player = User & {
}

const rm: Player = {
  name: "RM"
}

타입스크립트에게 오브젝트의 모양을 알려줄 때는 인터페이스를 사용하는게 좋은 듯! 왜냐하면 인터페이스가 좀 더 객체지향 프로그래밍 처럼 보이기 때문에 이해하기가 쉽다.

인터페이스의 또 다른 특징으로는 프로퍼티들을 축적시킬 수 있다.

interface User {
  name: string
}

interface User {
 nickname: string
}

interface User {
  hp: number
}

//다 합쳐서 사용가능
const rm: User = {
  name: "RM",
  nickname: "dudu",
  hp: 100
}

Interface & Class


추상 클래스의 문제점


//추상 클래스: 추상 클래스를 상속 받는 하위 클래스가 가질 프로퍼티와 메소드를 지정
abstract class User {
   constructor(
      protected firstName:string,
      protected lastName: string,
   ){}

   //추상 메서드 만들기
   //string으로 된 name 받아서 string 반환하기
   abstract sayHi(name: string):string
   //stirng 반환하기
   abstract fullName():string

}

//하위 클래스
class Player extends User {
   //추상 메서드 구현하기
   fullName(){
      return `${this.firstName} ${this.lastName}`
   }
    sayHi(name: string){
      return `Hi, ${name}~👋 My name is ${this.fullName}!`
   }
   //protected는 추상 클래스로부터 상속 받은 하위 클래스가 프로퍼티에 접근하도록 해준다.
}

const rm = new Player("Kim", "Namjoon")
  • 추상 클래스는 자신의 인스턴스를 만들 수가 없다.
    new User() 사용 불가

  • 추상 클래스는 단지 상속받는 클래스가 어떻게 동작해야 할지 알려주기 위해 사용된다.
    하위 클래스에서 무엇을 구현해야 할지 알려주기 때문에 추상 클래스는 꽤 좋다. sayHi(), fullName() 사용해야 한다고 알려준다.

  • 하지만 추상 클래스의 문제점은, 자바스크립트에 abstract의 개념이 없다는 것이다.
    User 인스턴스를 직접적으로 만들지 않지만, 컴파일된 JS 파일에는 User 클래스가 존재한다.

    • 컴파일된 JS 파일을 보면 추상 클래스가 일반 클래스로 바뀐 것을 확인할 수 있다.
      이처럼 TS에서 추상 클래스를 만들어도, JS로 컴파일되면 일반 클래스로 바뀌어버린다.
  • 그럼 추상클래스를 왜 만드는 걸까?
    다른 클래스들이 표준화된 모양, 표준화된 프로퍼티와 메서드를 갖도록 해주는 청사진을 만들기 위해 추상 클래스를 사용한다.

  • 이럴 때 interface를 사용하자.
    인터페이스는 가볍다. 인터페이스는 컴파일하면 JS로 바뀌지 않고 아에 사라져 버린다!!

  • 그럼 인터페이스를 사용하여, 클래스가 특정 형태를 따르도록 어떻게 강제할 수 있을까?

추상클래스를 인터페이스로 바꾸기: 추상 클래스 대신 인터페이스를 사용하여 클래스에 특정 프로퍼티나 메서드 상속하기


추상 클래스를 인터페이스로 바꾸기

  • 인터페이스에는 컨스트럭터가 없다.
  • 인터페이스는 오브젝트나 클래스의 모양을 묘사하도록 해준다.
interface User {
   firstName:string,
   lastName: string,
   
   //메서드 만들기
   //string으로 된 name 받아서 string 반환하기
   sayHi(name: string):string
   //stirng 반환하기
   fullName():string
}

하위 클래스

하위 클래스에서 인터페이스 상속하기: implements

  • extends를 쓰면 자바스크립트로 바뀐다.

  • 인터페이스를 상속할 때는 extends 대신 implements(구현)이라는 키워드를 사용하면 된다.
    그러면 코드가 더 가벼워진다. User 인터페이스가 타입스크립트에서만 존재하게 되므로 추적할수 없게 되기 때문이다.

  • 하지만 TS가 Player 클래스가 User 인터페이스를 상속해야 한다고 알려준다.

    따라서 클래스가 원하는대로 행동하고, 원하는 프로퍼티를 가지도록 강제하는 추상 클래스의 특징을 인터페이스를 사용해서도 사용할 수 있게된다.

  • 추상 클래스는 컴파일되면 JS의 클래스로 바껴버렸지만, 인터페이스는 컴파일되어도 JS의 클래스로 바뀌지도 않으면서 추상 클래스의 기능은 사용할 수 있다.

Player 클래스에 컨스트럭터를 만들고, 메서드를 구현해준다.

  • 인터페이스를 상속할 때는 프로퍼티를 public으로만 사용할 수 있나 봄...ㅇㅇ?
class Player implements User {
   //여기에 컨스트럭터 만들기
   constructor (
      public firstName:string,
      public lastName: string,
      ){}
   //인터페이스를 상속할 때는 프로퍼티를 private으로 만들 수 없음


   //메서드 구현하기
   fullName(){
      return `${this.firstName} ${this.lastName}`
   }
    sayHi(name: string){
      return `Hi, ${name}~👋 My name is ${this.fullName}!`
   }
}

//Player 인스턴스 생성
const rm = new Player("Kim", "Namjoon")

인터페이스를 사용하면 JS로 컴파일 됐을 때 추상 클래스를 추가로 사용하지 않기 때문에 코드가 좀 더 가볍다.

  • 파일 사이즈 줄이고 싶다면 인터페이스를 사용하면 된다.

  • 보다시피 인터페이스는 고유한 사용처가 있다. 클래스의 모양을 알려준다는 점에서 엄청 유용하다.
    그러면서도 추상 클래스를 사용할 때 처럼 자바스크립트 코드로 컴파일되지는 않는다.
    이게 인터페이스와 클래스의 차이점이다.

  • 그런데 인터페이스를 상속하는 것의 문제점 중 하나는 private, protected 프로퍼티를 사용하지 못한다는 점이다.

  • 그리고 추상 클래스에서는 컨스트럭터를 사용할 수 있었는데, 인터페이스에서는 컨스트럭터를 사용할 수 없다.


하나 이상의 인터페이스를 동시에 상속하기


원한다면 하나 이상의 인터페이스를 동시에 상속할 수 있다.

//인터페이스는 오브젝트나 클래스의 모양을 묘사하도록 해준다.
interface User {
   firstName:string,
   lastName: string,
   
   //메서드 만들기
   //string으로 된 name 받아서 string 반환하기
   sayHi(name: string):string
   //stirng 반환하기
   fullName():string
}

interface Human {
   hp: number,
}

//인터페이스 두 개 이상 사용하려면 이렇게 작성하면 된다.
class Player implements User, Human {
   constructor (
      public firstName:string,
      public lastName: string,
      public hp: number,
      ){}

   fullName(){
      return `${this.firstName} ${this.lastName}`
   }

    sayHi(name: string){
      return `Hi, ${name}~👋 My name is ${this.fullName}!`
   }
}

const rm = new Player("Kim", "Namjoon")

인터페이스를 타입으로 지정할 수도 있다.

클래스를 타입으로 사용할 수 있듯이, 인터페이스를 타입으로 사용할 수 있다.

interface User {
   firstName:string,
   lastName: string,
   
   //메서드 만들기
   //string으로 된 name 받아서 string 반환하기
   sayHi(name: string):string
   //stirng 반환하기
   fullName():string
}

//인터페이스를 타입으로 사용할 수도 있다.
//그리고 리턴 값에도 인터페이스를 타입으로 지정할 수 있다.
const makeUser(user: User): User{
  return {
    firstName: "kim",
    lastName: "namjoon",
    sayHi: (name) => "xxx"
    fullName: () => "xxx"
  } //리턴값에 User 인터페이스를 타입으로 지정하면,
//클래스에서 처럼 new Class() 로 작성안해도 된다. 그냥 인터페이스의 내용물만 넣어주면 된다.
}


makeUser({
  firstName: "kim",
   lastName: "namjoon",
   sayHi: (name) => "xxx"
   fullName: () => "xxx"
})
//뭐 이런식으로 인자에 인터페이스 사용하여 오브젝트 모양을 지정해 줄 수도 있다.

인터페이스 요약

  • 추상 클래스 대신 인터페이스를 사용하여 클래스에 특정 프로퍼티나 메서드 상속하도록 강제할 수 있다.
    인터페이스는 클래스가 아니지만, 클래스의 모양을 특정할 수 있게 해주는 간단한 방법이다.
    이처럼 인터페이스는 오브젝트의 모양을 결정지을 수도 있지만, 클래스의 모양을 특정 짓기도 한다.

  • 인터페이스는 추상 크랠스와 비슷한 보호를 제공하지만, 자바스크립트 파일에서는 보이지 않기 때문에 코드를 더 가볍게 할 수 있다.
    만약 추상 클래스를 다른 클래스들이 특정 모양을 따르도록 하기 위한 용도로 쓰고 있다면, 같은 역할을 하는 인터페이스를 사용하는 것이 더 좋다.

  • 그리고 한 클래스에서 여러 개의 인터페이스를 상속할 수도 있다.

  • 클래스처럼 타입으로 사용할 수 있다.
    이를 사용하여 아규먼트의 오브젝트 모양을 지정해 줄 수도 있고, 리턴 타입에 사용할 수도 있다.


type vs interface


오브젝트 모양 만들기

타입스크립트에게 오브젝트의 모양을 알려주고 싶다면 타입과 인터페이스를 사용할 수 있다.


//타입으로 오브젝트 모양 만들기
type PlayerA = {
   name: string
}

const PlayerA: PlayerA = {
   name: "rm"
}

//인터페이스로 오브젝트 모양 만들기
interface PlayerB {
   name: string
}

const PlayerB: PlayerB = {
   name: "rm"
}

그런데 둘의 목적은 동일하지만, 할 수 있는 것이 다르다.


상속하기


//타입으로 오브젝트 모양 만들기
type PlayerA = {
   name: string
}

//✅ 타입 상속하기
//PlayerAA 타입은 PlayerA 타입과 다른 오브젝트를 합친 것
type PlayerAA = PlayerA & {
   lastName: string
}

const PlayerA: PlayerAA = {
   name: "rm",
   lastName: "namjoon"
}

//////////////////////////////////////////////////////

//인터페이스로 오브젝트 모양 만들기
interface PlayerB {
   name: string
}

//✅ 인터페이스 상속하기: 객체지향
//Player B를 상속하는 PlayerBB 인터페이스 생성
interface PlayerBB extends PlayerB {
   lastname: string
}

const PlayerB: PlayerBB = {
   name: "rm",
   lastname: "namjoon"
}

결과는 같지만 과정이 좀 다르다.


타입과 인터페이스의 가장 큰 차이점: 새로운 프로퍼티 추가할 때!


타입과 인터페이스의 가장 큰 차이점은. 새로운 프로퍼티를 추가할 때 알 수 있다.

  • 새로운 프로퍼티 추가할 때 타입은 다시 선언될 수 없지만, 인터페이스는 항상 다시 선언될 수 있다. 즉, 항상 상속이 가능하다.
//타입으로 오브젝트 모양 만들기
type PlayerA = {
   name: string
}

//타입 상속하기
//PlayerAA 타입은 PlayerA 타입과 다른 오브젝트를 합친 것
//연산자 & 사용해야 함
type PlayerAA = PlayerA & {
   lastName: string
}

//✅ 프로퍼티 추가하기
//❌ 같은 이름의 타입 만들고 다른 프로퍼티 추가할 수 없음
//type PlayerAA = {
//  position: string
//}

const PlayerA: PlayerAA = {
   name: "rm",
   lastName: "namjoon"
}

//////////////////////////////////////////////////////

//인터페이스로 오브젝트 모양 만들기
interface PlayerB {
   name: string
}

//인터페이스 상속하기: 객체지향
//Player B를 상속하는 PlayerBB 인터페이스 생성
interface PlayerBB extends PlayerB {
   lastname: string
}

//✅ 프로퍼티 추가하기
//✅ 같은 이름의 인터페이스 만들고 다른 프로퍼티 추가할 수 있음!
interface PlayerBB {
  position: string
}

const PlayerB: PlayerBB = {
  name: "rm",
  lastname: "namjoon"
  position: "leader"
}

인터페이스는 같은 이름의 인터페이스 중복으로 만들 수 있기 때문에 아에 이렇게 사용해도 됨

//인터페이스로 오브젝트 모양 만들기
interface PlayerB {
   name: string
}

//굳이 상속하지 않고 추가해줘도 괜찮음
interface PlayerB {
   lastname: string
}

interface PlayerB {
  position: string
}

const PlayerB: PlayerB = {
  name: "rm",
  lastname: "namjoon"
  position: "leader"
}

type, interface 둘 다 클래스에 상속할 수 있고, 추상 클래스를 대체할 수 있다.

type PlayerA = {
  firstName: string
}

class User implements PlayerA {
  constructor(
  public firstName: string
  ){}
}

//////////////////////////////////////


interface PlayerB {
  firstName: string
}

class User implements PlayerB {
  constructor(
  public firstName: string
  ){}
}
  • 타입과 인터페이스가 모양이나 상속하는 방법, 새로운 프로퍼티 넣는 방법은 다르지만,
  • 오브젝트의 모양을 알리고자하는 목적은 같기 때문에 클래스에서 타입과 인터페이스 둘 다 상속할 수 있고, 추상클래스를 대체할 수 있다.

하지만~~~

📚 타입스크립트 커뮤니티에 따르면

  • 클래스오브젝트의 모양을 정의하고 싶으면 인터페이스를 사용하고
  • 다른 모든 경우에는 타입을 쓰라고 한다고 한다.

그러니까 타입스크립트에게 오브젝트의 모양을 알려주기 위해서는 인터페이스를 사용하도록 하자~

profile
Always have hope🍀 & constant passion🔥

0개의 댓글