타입스크립트를 공부하다보면 아마 대부분이 Interface와 Type의 역할이 거의 똑같은 데 왜 구분해서 사용하는지 의문이 들게 된다. 두 구문에 대한 예시는 다음과 같다.
[Interface, Type 사용 예시]
// Interface
interface withInterface {
propertyString: string
propertyNumber: number
}
const instanceInterface: withInterface = {
propertyString: 'Hello',
propertyNumber: 1
}
// Type
type withType = {
propertyString: string
propertyNumber: number
}
const instanceType: withType = {
propertyString: 'Hi',
propertyNumber: 2
}
console.log(`Interface: ${instanceInterface}`); // Ok
console.log(`Type: ${instanceType}`); // Ok
선언과 할당이 거의 비슷하게 작성되어지고 있어서 뚜렷한 차이가 없어 보인다. ⌜러닝 타입스크립트⌟에서는 객체 할당 같은 경우 많은 프로젝트에서 Interface를 사용한다고 간단하게 설명되어 있다. 어찌됐든, Interface와 Type을 따로 마려해 둔 이유는 있을 것이다. 그 실마리를 잡고자 글을 작성하며 정리해본다.
Type 별칭은
type 타입이름 = type형
의 형식으로 작성이 되어진다. 그리고 타입 어노태이션을 이용해서 변수에 할당 가능한 타입을 지정하는데 사용된다.
[Type의 타입 어노태이션]
type fruit = string;
const fruitName: fruit = 'banana'; // Ok
Type 별칭은 추가적인 기능은 없다. 변수에 할당될 수 있는 타입 지정이 길어지는 경우가 있는데, 이럴 경우 Type 별칭으로 따로 작성해두고 필요한 부분에 이름만 지정하면 쉽게 재사용이 가능해진다.
Interface는 Java의 Interface를 선언하고 사용하는 것처럼 작성되어진다.
interface 인터페이스명칭 {...}
의 형식이다. 그래서 사실 Interface는 type처럼 객체를 제외하고 사용할 수는 없는 것 같다. 한 번 실험해봤다.
[Interface 객체 없이 사용]
type withoutObjectType = string; // Ok
interface withoutObject = string;
// 'string' only refers to a type, but is being used as a value here.
interface withoutObject: string;
// 'string' only refers to a type, but is being used as a value here.
interface withObject {}
const instanceWithObj: withObject = {}; // Ok
결국 객체의 형태로 Type과 Interface를 사용하게 되면 기능상으로는 구분하기가 어려워지는 것 같다. 둘 다 타입 어노테이션을 통해서 타입을 지정해서 사용할 수 있다는 공통점도 가지고 있다.
그래도 구분해보고자 하는 노력으로 볼 수 있는 둘의 차이점은 하나는 할당의 형태로 작성이 되고 하나는 선언의 형태로 작성이 된다는 것이다.
Interface는 Type과 비교했을 때 추가적으로 제공해주는 기능이 있다. 확장(extends
) 기능이다. extends
만 놓고 보면 자바의 상속 기능과 비슷하게 느껴질 수도 있다.
[extends 예시]
interface withInterface {
propertyString: string
propertyNumber: number
}
interface withExtends extends withInterface {
propertyNumberAdded: number
}
const instanceInterface: withInterface = {
propertyString: 'Hello',
propertyNumber: 1
}
const wrongExtends: withExtends = {
propertyString: '안녕하세요',
propertyNumber: 2
}
// Property 'propertyNumberAdded' is missing in
// type '{ propertyString: string; propertyNumber: number; }'
// but required in type 'withExtends'.
const correctExtends: withExtends = {
propertyString: '안녕하세요',
propertyNumber: 3,
propertyNumberAdded: 4
} // Ok
Interface는 같은 스코프 안에서 동일한 이름을 갖는 Interface가 있다면 둘을 병합하여 사용할 수 있다. 다음의 예시로 확인할 수 있다.
/* Interface Merge */
interface withInterface {
propertyString: string
propertyNumber: number
}
interface withInterface {
propertyNumberAdded: number;
}
const correctExtends: withExtends = {
propertyString: '안녕하세요',
propertyNumber: 3,
propertyNumberAdded: 4
}
/* Type */
type withType = {
propertyString: string
propertyNumber: number
}
type withType = {
propertyNumberAdded: number
}
// Duplicate identifier 'withType'.
withInterface
는 작성되어야 하는 프로퍼티를 추가함으로써 병합이 된다. 하지만 withType
은 똑같은 식별자가 사용되고 있다는 경고문구를 출력하고 있다.
⌜러닝 타입스크립트⌟에서도 사실 개발에서 Interface 병합으로 이해하기 어려운 코드를 우려하여 권하지 않는 방법이라고 한다. 하지만 외부 패키지나 전역 인터페이스를 사용할 때 강점이 있다고 얘기한다.
프로젝트가 복잡해지고 다양한 타입스크립트의 기능을 사용하길 원한다면 Interface를 이용하는 것이 좋을 것 같다. 하지만 간단한 타입 지정은 Type과 Interface의 차이는 크게 없어 보인다.