개발/이펙티브 타입스크립트

[이펙티브 타입스크립트+13] 타입과 인터페이스의 차이점 알기

Junghyun Kim 2021. 8. 9. 23:30
반응형

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces

2장 타입스크립트의 타입 시스템
아이템 13 | 타입과 인터페이스의 차이점 알기

3줄 요약
1. 대부분의 경우에는 타입과 인터페이스는 차이점이 없다.
2. 복잡한 타입을 다룰 때는 type을 사용하는 것이 좋다.
3. 인터페이스의 선언 병합을 통해 속성을 확장시킬 수 있다.

참고) 이펙티브 타입스크립트는 타입스크립트의 기본 그 이상을 다룹니다.


타입스크립트에서 타입을 정의하는 방법은 두 가지가 있습니다.
첫 번째는 type을 이용하는 것이고,
두 번째는 interface를 이용하는 것입니다.

타입스크립트를 처음 공부해서 사용할 때 언제 타입을 사용해야 하는지 언제 인터페이스를 써야 하는지 몰라서 고민에 빠질 때가 있습니다.
대다수의 경우에는 타입을 사용해도 되고, 인터페이스를 사용해도 됩니다.
이번 아이템에서는 타입과 인터페이스의 공통점과 그리고 차이점에 대해서 알아보겠습니다.

공통점 1) 타입의 기본 동작
-> 당연하게도 type과 interface 모두 변수에 타입의 할당하는 등의 기본적인 동작들은 잘 됩니다.

type 혹은 interface로 타입을 선언할 수 있다.

공통점 2) 인덱스 시그니처
-> 인덱스 시그니처란 아래와 같이 생긴 형태를 의미합니다. 인덱스 시그니처에 관해서는 다음에 더 자세히 다루도록 하겠습니다.

type과 interface 모두 인덱스 시그니처를 사용가능하다.

공통점 3) 함수 타입 정의
-> 타입과 인터페이스 모두 함수 타입을 정의할 수 있습니다.

type과 interface 모두 함수 타입 선언 가능.

그리고 type의 경우에는 이보다 더 간단하게 type alias로 만들 수 있습니다.

type alias로 함수 타입 선언.

그런데 이렇게 놓고 보니 type alias가 정상적인 타입 선언 방법 같고 위에서 type과 interface로 선언한 방법이 조금 이상해 보입니다.
마치 오브젝트를 선언하는 방법 같습니다. 그러나 자세히 생각해보면서 이 또한 맞습니다.
자바스크립트에서는 함수 또한 오브젝트이니깐요.
그래서 아래와 같이 타입을 선언해도 정상입니다.

함수 또한 오브젝트이다.

공통점 4) 제너릭 가능
-> type과 interface 모두 제너릭 사용 가능합니다.

type과 interface 모두 제너릭 사용 가능.

공통점 5) 타입 확장 가능
-> type, interface 모두 확장 가능하며 서로 확장 가능합니다.

type과 interface 모두 확장 가능.

그러나 여기서 주의할 점이 있습니다.
인터페이스는 유니온 타입 같은 복잡한 타입을 확장하지 못합니다.
만일 복잡한 타입을 확장하고 싶다면 타입과 '&'(인터섹션)을 사용해야 합니다.

복잡한 타입에는 type을 사용해야 한다.

공통점 6) 클래스 구현 가능(implements)
-> 클래스를 구현(implements)할 때에는 type, interface 모두 사용 가능합니다

type, interface 모두 class를 만들 때 구현(implements)할 수 있다.


이제는 type과 interface의 차이점을 알아보겠습니다.

차이점 1) 유니온 개념의 유무
type에는 유니온 타입이 있지만, interface에는 유니온 인터페이스가 없습니다.

type TypeAorB = "a" | "b"

interface InterfaceAorB {
  ...?
}

또한, 앞서 언급한 것처럼 '&' 인터섹션 또한 존재하지 않습니다.
이로 인해 interface는 복잡한 타입을 만들어 낼 수 없습니다.

차이점 2) 튜플과 배열 타입의 간결한 표현
type 키워드를 이용하면 튜플과 배열 타입도 간결하게 표현할 수 있습니다.
interface를 사용하게 되면 유사하게 만들 수 있으나 튜플의 프로토타입 체인 상에 있는 메서드들을 사용할 수 없습니다.
단지, 유사품입니다.

type을 이용해서 만든 Tuple 타입. 여러 메서드들을 사용 가능.
interface를 이용하여 만든 유사 트리플 타입. 다양한 메서드들을 사용할 수 없음.

차이점 3) 선언 병합의 사용 가능 유무
interface에는 type에 없는 강력한 기능 하나를 가지고 있습니다. 그건 바로 '선언 병합(declaration merging)'이라고 하는 기능입니다.
선언 병합을 통해서 interface의 속성을 확장해나갈 수 있습니다. interface를 새로 만들지 않은 채로 말이죠.

이를 통해 기존의 interface의 속성을 추가해나갈 수 있습니다.
예시를 통해 살펴보겠습니다.
(오픈소스인 bootstrap의 type 관련 파일입니다.)

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/bootstrap/index.d.ts

이 코드는 @type/bootstrap 패키지를 설치하게 되면 나오게 되는 bootstrap의 타입을 저장해놓은 코드입니다.
첨부된 코드가 하는 내용은 기존의 글로벌 전역 변수에 있는 JQuery 타입과 Element 타입의 속성을 확장하고 있습니다.
JQuery나 Element 쪽의 라이브러리의 타입 코드를 수정하지 않고 bootstrap 쪽에서 필요한 속성을 추가하고 있습니다.
이렇게 기존의 타입의 속성을 확장하기에 선언 병합을 보강(augment)라고도 부릅니다.
(요즘 핫한 키워드인 AR라고 불리는 Augmented Reality의 Augmented와 같은 뜻입니다.)

이렇게 필요한 속성을 직접 추가할 수 있습니다.
타입스크립트에서 전역 변수를 추가할 때에도 유용하게 선언 병합을 사용할 수 있습니다.
전역 변수이기에 Window 타입에 속성을 더 해야 하기 때문입니다.

전역 변수에 속성을 추가할 때 에러가 난다.
Window 인터페이스 선언 병합을 통해 속성을 추가할 수 있다.

또한, es의 기능이 확장될 때에도 이러한 interface 선언 병합을 사용합니다.

정리하자면 type과 interface는 많은 부분에서는 큰 차이가 없습니다.
그러나 복잡한 타입을 다룰 때는 type을 이용하는 것이 좋고, 추후에 속성이 추가될 것을 고려하면 interface를 사용하는 것이 좋습니다.

Materials From
1. <이펙티브 타입스크립트>(댄 밴더캄 지음, 장원호 옮김, 인사이트 2021)
2. 타입스크립트 공식 홈페이지 | https://www.typescriptlang.org/

 

Typed JavaScript at Any Scale.

TypeScript extends JavaScript by adding types to the language. TypeScript speeds up your development experience by catching errors and providing fixes before you even run your code.

www.typescriptlang.org

 


참고)
인덱스 시그니처라는 개념을 몰랐을 때는 아래와 같은 에러를 만나면 당황하곤 했습니다.

someObject[key] 해당 부분에서 에러가 난다.

someObject의 키는 당연히 "name"과 "age"이기에 someObject의 인덱스로 접근했을 때 답이 나올 거라 생각했던 적이 있습니다.
그러나 에러가 났습니다. 에러의 내용은 다음과 같습니다.

string 타입은 someObject의 타입에 인덱스 시그니처가 아니다.

타입스크립트를 공부하기 전에는 해당 에러 내용을 이해를 못했습니다. 그러나 이제 우리는 이 에러를 이해할 수 있습니다.
할당받고자 하는 파라미터의 타입은 "name" 혹은 "hi"입니다. 타입으로 만들면 아래 "Parameters"와 같습니다.

type Parameters = "name" | "age"

반면에 할당하고자 하는 변수의 타입은 'string'입니다.
왜냐하면 Object.keys 메서드의 리턴 타입은 'string[]' 이기 때문입니다.

Object.keys 메서드의 함수 시그니처.

string 타입이 리터럴 타입의 유니온 타입보다 상위 집합이기에 할당이 되지 않았습니다.
인덱스의 자리에 string 타입이 할당이 되지 않는 것이죠.

이를 해결하기 위해서는 오브젝트 접근자의 키의 타입을 단언하는 방법이 있고, 인덱스 시그니처를 사용하는 방법이 있습니다.

타입 단언을 통해 오브젝트에 접근.
인덱스 시그니처를 통해 에러 해결.

인덱스 시그니처를 통해서 하는 방법은 타입을 만들어야 합니다.
이를 통해 앞서 나온 에러를 해결할 수 있습니다만 인덱스 시그니처를 사용하기 전에는 고려해야 할 사항들이 있습니다.
해당 내용들은 추후 아이템 15에서 다루도록 하겠습니다.

반응형