Type alias vs interface

myung hun kang·2023년 10월 9일
0

Type alias vs interface

서론

타입스크립트를 사용하면 특정 객체나 값에 대하여 어떤 타입의 값을 가지고 있는지 설정해야하는 일이 있다.

가령 예를들면,


interface Person {
 name: string;
 lang: string;
 legs: boolean;
}

또는


type Person = {
 name: string;
 lang: string;
 legs: boolean;
}

처럼 말이다.

여기서 person은 두가지 typeinterface 로 정의되었다.

type을 정의한 방법이 다르니 다른 방식으로 typescript에 적용될 것 같지만,

실제로 사용되는 측면에서는 둘 다 문제없이 잘 사용할 수 있고, 거의 같다고 볼 수 있다.

typescript 공식문서를 보면

다음과 같은 글을 볼 수가 있다.

There are two main tools to declare the shape of an
object: interfaces and type aliases.

They are very similar, and for the most common cases
act the same.

한마디로 거의 비슷하다 라는 뜻!

그런데 typescript는 왜 이 두가지 방식을 제안하는걸까?

이번 글에서는 위 두가지 방식에 차이점에 대해 알아보고 어떤 것을 사용하는 것이 좋을 지 개인적인 생각을 남기려 한다.

본문

1. 선언방식

앞서 예시에서 본 것처럼 선언하는 방식에 차이가 있다.

interface는 객체로서 선언을 해야하지만


interface Person {
 name: string;
 lang: string;
 legs: boolean;
}

type은 객체 뿐만 아니라 string, boolean, array, number 등 다양하게 선언할 수 있다.


type Person = {
 name: string;
 lang: string;
 legs: boolean;
}

type A = number;
type B = string[];

interface에서 위 A 와 같은 타입을 지정하려면?


interface A {
	a: number
}

이런식으로만 타입지정이 가능하다.

2. 확장

지정한 타입을 확장하는 경우 차이를 보자


interface Person {
 name: string;
 lang: string;
 legs: boolean;
}
// 또는 
type Person = {
 name: string;
 lang: string;
 legs: boolean;
}
// 로 지정한 타입이 있을 경우

위 기능에 더불어 나라별로 인사말을 다르게 가지도록 확장해서 사용한다고 해보자

interface의 경우는 extends 키워드를 사용하여 확장할 수 있다.


interface PersonUSA extends Person{
 greeting: "hi"
}

type의 경우는 intersection types 를 사용하면 된다.


type PersonUSA = { greeting: "hi" } & Person

단순히 보기에 큰차이는 없어보인다.

필자는 개인이 선호하는 방식을 사용하면 된다고 생각하는데 문서에서는 다음과 같은 설명을 넣었다.

That said, we recommend you use interfaces over type aliases.
Specifically, because you will get better error messages.
If you hover over the following errors, you can see how TypeScript can provide terser and more focused messages when working with interfaces

에러 메시지를 좀 더 명료하고 간결하게 확인하고 싶다면 interface를 추천한다는 말이다.

실제 error 메시지의 예시는 typescript playground에서 위 type vs interface에 대한 예시에서 확인할 수 있다.

그치만 직접가서 보기는 번거로우니 일단 스샷을 가져오도록 하겠다.

예시코드

// 타입 지정
type BirdType = {
  wings: 2;
};

interface BirdInterface {
  wings: 2;
}

// Owl 타입은 intersection 으로 
type Owl = { nocturnal: true } & BirdType;

// Chicken은 extends로 
interface Chicken extends BirdInterface {
  colourful: false;
  flies: false;
}

let owl: Owl = { wings: 2, nocturnal: true };
let chicken: Chicken = { wings: 2, colourful: false, flies: false };

owl = chicken;
chicken = owl;

이와 같은 상황에서 owl에 chicken을 넣어도 error가 발생하고 chicken에 owl을 넣어도 error가 발생한다.

에러 메시지는 다음처럼 제공된다.

owl 에서의 에러 메시지
type error

owl은 type으로 지정된 Owl 타입이어서 설명이 길다.

chicken에서의 에러 메시지

interface error

chicken은 interface으로 지정된 chicken 타입이어서 설명이 간결하다. 

타입스크립트 문서에서 이와 관련해서는 interface를 추천하지만 솔직히 필자가 보기에는 외국어라서 그런지 별차이 없어 보인다.

3. 유틸리티 타입

지정한 타입에서 특정 타입만 빼거나, 또는 특정 타입만 선택하거나 등등 타입스크립트에서는 다양한 방식으로 타입을 지정할 수 있도록 유틸리티 타입을 사용할 수 있게 해준다.

Omit의 경우를 예로 들어보면

위 person 타입에서 legs 값만 빼고 싶다고 해보자

interface의 경우

interface PersonLegs extends Omit<Person, "legs"> {}

와 같고

type의 경우는

type PersonLegs =  Omit<Person, "legs">

가 된다.

Pick 도 비슷하리라

interface의 경우는 좀 번거로워보인다.

4. 튜플

튜플의 타입을 지정할 때 차이가 확연하다.

[string, number] 두가지 값을 가지는 배열 타입을 지정하려고 할 때

interface의 경우

interface arr extends Array<string | number> {
  0: string;
  1: number
}

let tuple : arr = ["asef", 1] // -> ok

type의 경우

type arr =  [ string, number ]

let tuple : arr = ["asef", 1] // -> ok

type으로 지정이 너무 명확히 쉽다.

5. 선언병합

type과 interface의 가장 큰 차이점은 역시 선언병합에 열려있는지 닫혀있는지일 것이다.

interface의 경우 Person 타입에 새로 hands 타입을 추가하고 싶다면


interface Person {
 name: string;
 lang: string;
 legs: boolean;
}

interface Person {
 hands: boolean;
}

위처럼 같은 이름에 새로 지정하여 새로운 타입을 얹힐 수 있다.

반면 type은 위처럼 하면 error가 난다.

결론

필자는 위 두가지 방식에 대해서 깊이 고민하지않고 interface를 사용하였다.

왜냐하면 typescript에서 위 부분에 대한 많은 사람들의 고민을 알고 있는지 다음과 같은 말을 문서에 넣었기 때문이다.

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type.

근데 지금와서 다시 생각해보니 type을 이용하는 것이 좋아보인다.

아직 두가지 방식을 사용하는 측면에서 문제가 될만한 error를 만난적은 없다.

하지만 interface를 사용하면 선언병합이라는 부분에 조심해서 사용해야할 것 같다.

따라서,

기본적으로 type를 사용하고, 라이브러리나 모듈을 사용할 때 해당 기능을 프로젝트에 맞게 확장해 사용하고 싶을때 interface를 사용하는 것이 맞지 않을까 싶다.

근데 여태 interface를 주로 사용해서 또 고민이다......

profile
프론트엔드 개발자입니다.

0개의 댓글