본문 바로가기
개발/타입스크립트

[타입스크립트] 인터페이스 vs 타입 에 대하여

by 핸디(Handy) 2021. 7. 12.

제가 배운 바로는 타입스크립트는 자바스크립트에 타입이 추가된 슈퍼셋 언어라고 배웠습니다. 그래서 타입을 선언할 때 interface 대신에 type를 쓰는 것이 뭔가 옳아 보인다고 생각하기도 했습니다.

하지만 어쩔때는 type보다 interface를 많이 쓰는 것을 보고 어느 것이 옳은 것인가 고민을 하다 제가 선택한 방법은

누군가 type으로 써놓으면 나도 type으로 선언하고 interface가 많으면 interface를 쓴다. 

제가 이러한 방법을 생각한 이유는 적어도 일관성은 유지되지 않을까 라는 생각과 복붙의 용이성 때문이었습니다.

그 후에 이펙티브 타입스크립트를 읽으며 습득한 지식에 따르면 다행스럽게도 폐급 수준은 아니었음을 확인하였습니다.

책의 내용을 두괄식으로 요약하자면 저자 형님은 3가지를 강조하셨습니다.

  1. 타입과 인터페이스의 차이점과 비슷한 점을 이해한다.
  2. 한 타입을 type과 interface로 두 가지 문법으로 사용하는 방법을 알아야 한다.
  3. 어떤 문법을 사용할지 결정할 때 일관된 스타일을 확립하고 보강기법을 필요한지 고려해라.

적어도 일관된 스타일을 추구했으니 다행이라고 생각하며 이번 포스트에서는 3가지를 하나씩 따라가 보려고 합니다.


일단 타입스크립트에서는 명명된 타입 (named type)를 선언하는 방법은 두 가지입니다.

이게 바로 type과 interface입니다. 예시를 한번 보도록 하겠습니다.

type t_stock = {
  name: string;
  price: number;
};

interface i_stock {
  name: string;
  price: number;
}

const apple: t_stock = {
  name: "Apple",
  price: 100,
};

const tesla: i_stock = {
  name: "Tesla",
  price: 100,
};

같은 역할을 하는 것을 확인하였고 타입스크립트가 주는 편안함도 비슷합니다.

만약 ticker라는 key를 추가적으로 넣는다고 가정해보겠습니다. 그러면 둘 다 모두 ticker라는 property가 해당 type에 없다고 나오는 걸 확인할 수 있습니다.

type에 잘못된 key를 넣을때
interface에 잘못된 key를 넣을때

따라서 우리는 코드를 칠 때 "아 해당 타입에는 ticker가 없구나"라는 정보를 빠르게 얻을 수 있습니다.

만약 ticker를 넣고 싶다면 어떻게 해야 할까요?

바로 interface는 extends로 확장하고 type은 &(Intersection)으로 추가하면 됩니다.

type 과 interface의 확장

그리고 기능적으로 유사한 친구들은 서로를 확장할 수도 있습니다.(cross로 가능하다) 이렇듯 둘은 매우 유사한 느낌을 줍니다.

cross로 확장

이 외에도 제네릭, 클래스 구현할 때도 같은 방식으로 선언되고 동작합니다.


그렇다면 type과 interface의 차이점은 무엇일까?

첫 번째로는 조합이라고 볼 수 있습니다.

type은 유니온, 인터섹션 등 타입끼리의 연산이 가능하다는 것입니다. 따라서 이러한 이점은 타입스크립트에서 타입을 선언하는 데에 빠른 속도를 제공합니다. 특정 타입을 빼버리거나 추가하는 게 상대적으로 간편하고 타입스크립트에서도 이러한 기능을 제공하고 있습니다.

두 번째로는 보강(augment)이 있습니다.

보강이란 이름 그대로 채워서 강하게 만드는 것입니다. 예시를 한번 살펴보겠습니다.

위에서 우리는 interface는 extends로 type은 &를 사용해 ticker라는 key를 확장했습니다.

하지만 상상해보겠습니다. 우리가 다른 사람의 interface, type를 가져와 사용할 때 특정 객체의 값은 이미 선언되어있습니다. 이럴 때마다 extend 또는 &타입으로 확장하는 건 매우 번거로운 일입니다. 이러한 불편함은 외부 라이브러리를 가져와 쓸 때 더욱 두르러집니다. 

대부분의 라이브러리들이 자바스크립트 기준으로 작성되었고 이러한 라이브러리들을 타입스크립트에서 사용하기 위해 타입 선언 파일을 두고 한번 걸러서 가져오게 됩니다. 하지만 대부분의 라이브러리들이 제공하는 객체에 대한 모든 타입을 하나씩 빠트리지 않고 선언하는 일은 매우 힘든 일입니다. 

따라서 여기서 제공하는 트릭은 큰 틀에서 interface로 커버해놓고 작은 것들은 interface를 한번 더 선언함(보강)으로써 부족한 타입을 추가해주는 것입니다.

또한 책에서 Array로 예시를 들었습니다.

Array interface는 lib.es.5.d.ts에 정의되어 있는데 우리가 tsconfig에서 target를 변경한다면 해당 d.ts에 정의되어있는 Array interface에서 충돌이 납니다. 하지만 보강이 있다면 추가적으로 확장하는 것으로 끝나고 별다른 변경사항은 없게 되는 것입니다.

그래서 타입 선언 파일은 반드시 interface를 사용해야 하는 표준이 있다고 합니다.


최종적으로 마무리해보겠습니다.

type과 interface의 기본적인 기능 차이는 거의 없다고 생각합니다. 따라서 더 편한 것을 써도 됩니다.

하지만 위에서 설명한대로 코드의 일관성확장의 여부를 신경써야합니다. 

다시 말해 기존의 코드를 따라가되, 추가적인 코드를 구현함에 있어서 기준을 정해놓고 사용하는 것입니다.

제가 세운 기준은 다음과 같습니다.

  • type : 개별 또는 내부적으로 선언되는 타입, 타입 내부의 변경보다는 다른 타입끼리의 조합등에 사용될 경우
  • interface : 변경 가능성이 높은 타입, api의 input, output 값처럼 외부로 연결 또는 확장되는 경우

정답은 아직 뭔지 모르겠습니다. 스택오버플로우 형님들이 말하는건 아직 제 수준으로 와닿지 않고, 그래서 이러한 기준을 세우고 사용하면서 기준을 좀더 날카롭게 세우려고 합니다.

p.s 해당 글은 이펙티브 타입스크립트 -댄 밴더캄 中 아이템 13을 참고하였습니다.

댓글