TIR: Typescript | 클래스와 인터페이스 (5) 인터페이스

Lumpen·2023년 1월 23일
0

Typescript

목록 보기
7/17

인터페이스

클래스는 인터페이스를 통해 사용할 때가 많다
타입 별칭과 같이 인터페이스도 타입에 이름을 지어주는 수단으로 인터페이스를 사용하면 타입을 더 깔끔하게 정의할 수 있다

타입 별칭과 인터페이스는 문법만 다르고 거의 같은 기능을 수행한다
몇몇 부분에서만 차이를 보인다

타입과 인터페이스

type Sushi = {
	calories: number
  	salty: boolean
  	tasty: boolean
}

interface Sushi {
	calories: number
  	salty: boolean
  	tasty: boolean
}

둘 다 형태를 정의하며 두 형태 정의는 서로 할당할 수 있다 (실제로 둘은 같다)

여러 음식을 정의하기 위해 Food 라는 타입으로 고ㅜㅇ통된 정보를 정의하고 다른 음식들을 재정의 해보자

type Food = {
	calories: number
  	tasty: boolean
}

type Sushi = Food & {
	salty: boolean
}

type Cake = Food & {
	sweet: boolean
}

위 코드를 인터페이스로도 똑같이 정의할 수 있다

interface Food {
	calories: number
  	tasty: boolean
}

interface Sushi extends Food {
	salty: boolean
}

interface Cake extends Food {
	sweet: boolean
}

인터페이스는 객체 타입, 클래스, 다른 인터페이스 모두 상속 받을 수 있다

타입과 인터페이스의 차이

타입 별칭의 오른편에는 타입 표현식을 포함한 모든 타입이 등장할 수 있지만 인터페이스에는 반드시 형태가 나와야 한다

type A = number
type B = A | string

이와 같은 형태는 인터페이스로 작성할 수 없다

인터페이스 상속 시 타입스크립트는 상속받는 인터페이스의 타입이 상위 인터페이스를 할당할 수 있는지 확인한다

interface A {
	good(x: number): string
    bad(x: number): string
}

interface B extends A {
	good(x: string | number): string
    bad(x: string): string // error
}

인터페이스 B 에서 A 를 상속 시 타입이 매개변수 타입이 달라서 bad() 의 경우 에러가 발생한다

위 예에서 인터페이스를 타입 별칭으로 변경 후 extends를 인터섹션(&) 으로 변경하면 확장하는 타입을 조합하는 방향으로 동작한다
bad()를 오버로드한 시그니처가 만들어진다

  • 이름과 범위가 같은 인터페이스가 여러개 있다면 자동으로 합쳐진다 (선언 합침)

선언 합침

declaration merging (선언 합침) 은 같은 이름으로 정의된 여러 정의를 자동으로 합치는 타입스크립트의 기능이다

interface User {
	name: string
}

interface User {
	age: number
}

위의 User 인터페이스는 다음과 같다

interface User {
	name: string
  	age: number
}

한 편 인터페이스끼리는 충돌할 수 없다
오버라이드 되지 않는다

interface User {
	name: string
}

interface User {
	name: number // error
}

같은 인터페이스 내에서 한 번 선언된 name 프로퍼티는 다른 인터페이스에서도 동일한 타입을 가져야 한다

제네릭 선언 시 제네릭의 선언과 이름까지 똑같아야 한다

interface User<Age extends number>{
	age: Age
}

interface User<Age extends string> {
	age: Age
}

위 경우에도 extends 되고있는 타입이 다르기 때문에 에러가 발생한다

구현

클래스 선언 시 implements 라는 키워드를 이용해
특정 인터페이스를 만족시킴을 표현할 수 있다
다른 명시적 타입 어노테이션 처럼 implements로 타입 수준의 제한을 추가하면 구현에 문제가 있을 시 쉽게 파악할 수 있다
디자인 패턴을 구현하는 대표적 방식이 되기도 한다

interface Animal {
	eat(food: string): void
  	sleep(hours: number): void
}

class Cat implements Animal {
  eat(food: string) {
  	//
  }
  
  sleep(hours: number) {
  	//
  }
}

Cat 은 Animal 에서 선언된 모든 메소드를 구현해야 하며 프로퍼티나 메소드를 추가할 수 있다

인터페이스로 인스턴스 프로퍼티를 정의할 수 있지만
가시성 한정자 (private, protected, public)는 선언할 수 없으며 static 도 선언할 수 없다
인스턴스 프로퍼티를 readonly 로는 선언 가능

한 클래스가 여러 인터페이스를 구현할 수도 있다

class Cat implements Animal, Feline {}

모든 기능은 완전한 타입 안전성을 제공한다

인터페이스 구현 vs 추상 클래스 상속

인터페이스 구현은 추상 클래스 상속돠 비슷하다
하지만 인터페이스 구현은 가볍고 범용으로 쓰이지만
추상 클래스는 특별한 목적과 풍부한 기능을 갖는다

인터페이스는 형태를 정의하는 수단으로
값 수준에서 객체, 배열, 함수, 클래스, 클래스 인스턴스를 정의할 수 있다
아무런 자바스크립트 코드를 만들지 않고 컴파일 타임에만 존재한다

추상 클래스는 클래스만 정의할 수 있다
추상 크랠스는 런타임에 자바스크립트 클래스 코드를 만든다

추상클래스는 생성자와 기본 구현을 가질 수 있고 프로퍼티와 메소드에 접근 한정자를 지정할 수 있다

여러 클래스에서 공유하는 구현이라면 추상 클래스
단순히 클래스를 정의하는 것이라면 인터페이스 사용

profile
떠돌이 생활을 하는. 실업자는 아니지만, 부랑 생활을 하는

0개의 댓글