![thumbnail](/static/1cd0632ed7f22fe4ee20faf67c5ec813/b1abc/230418_interface-type-thumbnail.png)
객체 타입을 선언하는 방식에는 interface(인터페이스)와 type alias(타입 별칭)가 있다. 두 방식은 비슷하게 작동하면서도 분명한 차이가 존재한다.
타입 선언 방식
아래는 타입 별칭과 인터페이스로 객체 타입을 선언하는 구문이다.
// Type alias
type Info = {
name: string;
age: number;
};
// Interface
interface Info {
name: string;
age: number;
}
선언한 타입에 맞지 않는 속성값을 할당하면 두 방식 모두 에러 메시지를 표시한다. 할당 가능성 오류가 거의 동일하게 작용한다는 것을 알 수 있다.
type Info = {
name: string;
age: number;
};
let user: Info = {
name: 'choco',
age: 3,
};
user = {
name: true, // error: Type 'boolean' is not assignable to type 'string'.
age: 5,
};
interface Info {
name: string;
age: number;
}
let user: Info = {
name: 'choco',
age: 3,
};
user = {
name: true, // error: Type 'boolean' is not assignable to type 'string'.
age: 5,
};
타입 별칭과 인터페이스 비교
읽기 쉬운 오류 메시지
오류가 발생한 코드에 커서를 올려보자. 전체적인 오류 메시지는 같지만, 인터페이스는 타입 별칭과 달리 항상 속성 앞에 지정한 이름이 붙여 표시된다.
![type alias error message type alias error message](/static/2696a673a7f51466bf1edbb71308ccc6/5d5ad/230418_type-error-message.png)
![interface error message interface error message](/static/02ff1e8f5a933f2f81b347de8a60ad2e/5d5ad/230418_interface-error-message.png)
타입 별칭은 이름 없는 객체 리터럴의 단순한 별칭이지만 인터페이스는 이름이 있는 객체로써 간주되기 때문에 특수한 케이스에서 발생하는 오류 메시지를 읽기 쉽다는 장점이 있다.
선언 병합(declaration merge)
타입 별칭과 달리 인터페이스는 속성을 늘리기 위해 병합을 실행할 수 있다. 같은 스코프 내에서 동일한 이름의 두 인터페이스가 선언된 경우에 선언된 모든 속성을 가진 더 큰 인터페이스가 코드 내부에 추가된다.
interface Info {
name: string;
}
interface Info {
age: number;
}
// 아래와 동일
// interface Info {
// name: string;
// age: number;
// }
let user: Info = {
name: 'choco',
age: true, // error: Type 'boolean' is not assignable to type 'number'.
};
반면에 타입 별칭에서는 동일한 이름으로 선언한 경우, 중복된 식별자라며 에러 메시지가 표시된다.
type Info = {
name: string;
};
type Info = {
// error: Duplicate identifier 'Info'.
age: number;
};
동일한 이름으로 다수의 인터페이스를 선언하는 것은 코드를 이해하는 데 어려움을 줄 수 있기 때문에 인터페이스 병합은 가능한 지양하는 것이 좋다. 그러면 어떤 상황에서 사용하는 것이 좋을까.
바로 npm과 같은 외부 패키지나 Window 같은 내장된 전역 인터페이스를 사용할 때 이 선언 병합이 매우 유용하게 쓰일 수 있다!
interface Window {
title: string;
}
interface Window {
ts: TypeScriptAPI;
}
const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});
primitive 확장
타입 별칭은 string과 같은 원시 타입에 대한 별칭을 만들 수 있지만 인터페이스는 불가능하다. 예를 들어 타입 별칭으로 항상 string 타입인 ‘Name’이라는 별칭을 선언한다면 아래와 같이 작성할 수 있다.
type Name = string;
그러나 인터페이스로 동일한 기능을 수행하려고 하면 작동하지 않는다.
interface Name extends string {}
// error: An interface cannot extend a primitive type like 'string'; an interface can only extend named types and classes
// 인터페이스는 'string'과 같은 기본 형식을 확장할 수 없습니다. 인터페이스는 명명된 형식 및 클래스만 확장할 수 있습니다
union(유니언) 타입
타입 별칭은 유니언 타입을 선언할 수 있지만 인터페이스는 할 수 없다.
type Id = string | number;
결론
위 내용을 표로 한눈에 정리해 보면 아래와 같다.
특징 | Interface | Type | e.g. |
---|---|---|---|
declaration merge | interface Window {…} | ||
primitives | type Name = string; | ||
union | type Id = string | number; | ||
extend / intersect | Name & Id |
타입 별칭과 인터페이스는 거의 유사하게 동작하므로 취향에 따라 자유롭게 사용할 수 있다. 타입스크립트에서는 가능한 인터페이스 사용을 권장하고 있기 때문에 primitive, 유니언, 튜플 등을 사용할 때는 타입 별칭을 사용하고 그 외에는 인터페이스를 사용하는 것이 좋다는 의견이다.
또한, 경우에 따라 인터페이스가 타입 별칭보다 성능이 더 뛰어나다고 하니 아래 링크에 방문해 읽어보는 것도 좋겠다.