[Typescript] Type Alias vs Interface
Typescript 프로젝트를 진행하기 앞서, 토이클론을 진행하며 객체 등 타입선언에 Type, Interface 모두를 사용하였다.
Type Alias는 "="을 같이 사용하여 선언하고, Interface는 불필요하다는 간단한 Literal 적 차이점부터,
Interface는 Class처럼 확장이 가능하고, Type은 다르다는 등 둘의 비슷하면서도 다른점들을 얼핏 보아왔다.
하지만, 분명 Typescript에서 이 둘을 구분한 이유가 있을 것이며, 그렇기에 각각이 쓰이기에 적절한 경우가 있을 것이다.
이를 많은 소스들을 참고하면서 정리해보고자 본 포스팅을 올린다.
💙 개념
- Type Alias(타입 별칭)
// string 타입을 사용할 때
const name: string = 'capt';
// 타입 별칭을 사용할 때
type MyName = string;
const name: MyName = 'capt';
Type Alias는 특정 타입이나 인터페이스를 참조할 수 있는 타입 변수를 선언하는 키워드이다.
그렇기에, 새로운 타입의 생성보다는 정의한 타입들을 이후에 쉽게 참고할 수 있도록 네이밍하는 개념인 것이다.
* Type Alias 특징 : 프리뷰
핸드북에 소개된 내용으로, 아래는 Interface와 Type Alias 각각을 VSCode 프리뷰로 확인하는 이미지이다.
Interface는 마치 하나의 새로운 타입모음으로 정의된 느낌이라면, Type Alias는 각각의 타입들을 직관적으로 보여준다.
* 더 알아보기 (이전 포스팅) : https://abangpa1ace.tistory.com/97?category=927153
- Interface(인터페이스)
인터페이스의 의미 자체는 상호간에 정의한 약속 혹은 규칙을 의미한다. Typescript의 인터페이스는 아래의 범주에 대한 약속을 정의한다.
- 객체의 스펙(속성, 속성의 타입)
- 함수의 파라미터, 반환값의 타입
- 배열과 객체를 접근하는 방식
- 클래스
interface CraftBeer {
name: string;
hop?: number;
}
let myBeer = {
name: 'Saporo'
};
function brewBeer(beer: CraftBeer) {
console.log(beer.brewery); // Error: Property 'brewery' does not exist on type 'Beer'
}
brewBeer(myBeer);
인터페이스는 위처럼, 타입 명칭과 유사하게 문법적 전개가 가능하다.
또한, 옵션속성(?), 읽기전용 속성(readonly) 등의 기능과, 함수 및 클래스에 대한 타입도 정의할 수 있다.
// 함수 타입
interface login {
(username: string, password: string): boolean;
}
let loginUser: login;
loginUser = function(id: string, pw: string) {
console.log('로그인 했습니다');
return true;
}
// 클래스 타입
interface CraftBeer {
beerName: string;
nameBeer(beer: string): void;
}
class myBeer implements CraftBeer {
beerName: string = 'Baby Guinness';
nameBeer(b: string) {
this.beerName = b;
}
constructor() {}
}
* Interface 의 특징 : 확장(extends)
클래스와 마찬가지로 인터페이스도 인터페이스 간의 상속과 확장이 가능하다. extends 키워드를 동일하게 사용하면 된다.
interface Person {
name: string;
}
interface Developer extends Person {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
* 더 알아보기 (이전 포스팅) : https://abangpa1ace.tistory.com/98?category=927153
💙 Type Alias vs Interface 비교
본격적으로, Type Alias(타입별칭) 과 Interface(인터페이스) 의 공통점 / 차이점을 비교하고 각각의 용도를 구체화해보았다.
- 공통점
1. 새로운 타입의 이름을 짓기
어쩌면 이 둘이 가장 많이 사용되는 이유이자, 그렇기에 구분하기 힘들어지는 상황일 것이다.
객체 등의 복합적인 데이터 구조에 대한 타입을 새로이 명시하기 위해, 이 둘이 사용되기 때문이다.
interface Human {
name: string;
age: number;
}
type Human = {
name: string;
age: number;
};
2. 여러 타입에 대한 관계정의
둘 다 새로운 Interface에 대한 extends, 클래스에 대한 implements 키워드를 통해 관계정의가 가능하다.
단, 객체 타입 혹은 객체 타입간의 곱타입(Intersection Type, 교차 타입), 즉 정적인 형태의 객체타입만 동작한다.
* 이는, 합타입(Union Type) 은 extends, implements 대신 다른 방법을 사용해야함을 암시한다.
type TBase = {
t: number;
};
interface IBase {
i: number;
}
// extends 사용
interface I1 extends TBase {}
interface I2 extends IBase {}
// implements 사용
class C1 implements TBase {
constructor(public t: number) {}
}
class C2 implements IBase {
constructor(public i: number) {}
}
// 곱 타입에 대한 extends, implements 사용
type TIntersection = TBase & {
t2: number;
};
interface I3 extends TIntersection {}
class C3 implements TIntersection {
constructor(public t: number, public t2: number) {}
}
interface I4 extends I3 {}
class C4 implements I3 {
constructor(public t: number, public t2: number) {}
}
// 오류: 합 타입에 대한 extends, implements 사용
type TUnion = TBase | {
u: number;
};
interface I5 extends TUnion {}
// error(TS2312)
// An interface can only extend an object type
// or intersection of object types with statically known members.
class C5 implements TUnion {}
// error(TS2422)
// A class can only implement an object type
// or intersection of object types with statically known members.
view raw
- 차이점
1. Type 확장
Type Alias는 &로 타입간의 연결형태를 보임에 비해, Interface는 클래스처럼 extends 키워드를 통해 확장이 가능하다.
// Interface 확장
interface PeopleInterface {
name: string
age: number
}
interface StudentInterface extends PeopleInterface {
school: string
}
// Type Alias 확장
type PeopleType = {
name: string
age: number
}
type StudentType = PeopleType & {
school: string
}
2. Interface 의 선언 병합
Interface는 동일한 이름으로 여러번 선언해도, 컴파일 시점에서 하나로 합쳐진다. 이를, 선언 병합(Declaration Merging) 이라 한다.
interface Window {
title: string;
}
interface Window {
ts: import("typescript");
}
declare function getWindow(): Window;
const window = getWindow();
const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {}); // transpileModule() 메서드 사용 가능
3. Interface는 객체만 사용가능
interface FooInterface {
value: string
}
type FooType = {
value: string
}
type FooOnlyString = string
type FooTypeNumber = number
// 불가능
interface X extends string {}
4. Type Alias는 Computed Value의 적용이 가능
type names = 'firstName' | 'lastName'
type NameTypes = {
[key in names]: string
}
const yc: NameTypes = { firstName: 'hi', lastName: 'yc' }
interface NameInterface {
// error
[key in names]: string
}
💙 결론
공식문서를 비롯한 대부분의 의견이 그러하듯, Type Alias 보다는 Interface의 사용을 권장한다.
대표적인 이유는, extends 키워드를 통한 유려한 확장성과, 선언 병합을 통해 타입설정의 오류가 최소화되기 때문이다.
다만, union 혹은 tuple 등이 적용되어야 하는 경우, key-value에 제네릭이 적용되는 경우 등에는 Type Alias가 유리할수도 있다.
내 나름대로 내린 결론은, API 및 데이터 스트럭쳐 등 기본적으로 객체형태면서 각자 연계가 되는 경우 Interface가 좋다고 생각한다.
Props, State의 경우가 고민이 많다. 기본적으로 Props의 형태가 반복될 수 있기 때문에 Interface가 좋을수 있다.
하지만, (규모가 상대적으로 작을때이겠지만) 각 컴포넌트 Props의 형태는 정적으로 관리가 되는게 좋다고 생각되기 때문에, 정적 타입 및 Interface로 조합된 type으로 선언하는 것이 나은 형태일수도 있따는 생각도 든다.
[출처]
- Typescript 공식 Github : https://typescript-kr.github.io/pages/tutorials/typescript-in-5-minutes.html
- Typescript 핸드북 : https://joshua1988.github.io/ts/guide/type-inference.html
- https://yceffort.kr/2021/03/typescript-interface-vs-type
- https://jungpaeng.tistory.com/99
- https://joonsungum.github.io/post/2019-02-25-typescript-interface-and-type-alias/