Type Aliases VS Interfaces

정유정 | yujeong choung·2022년 9월 12일
1

Typescript

목록 보기
1/1
post-thumbnail

⭐️ 틀린 내용이 있다면 언제든지 댓글로 알려주세요!

타입스크립트를 쓰면서 가장 먼저 접하는 것은 당연하게 타입일 것이다. 필자는 자바스크립트만 사용하다가 타입스크립트를 처음 사용하게 됐을 때 타입을 선언한다라는 개념이 익숙하지 않아서 많이 헷갈려 했던 기억이 난다.

차근차근 타입의 종류를 배운 이후에 가장 먼저 궁금증을 가졌던 부분이 Type Alias (타입 별칭) 와 Interface (인터페이스) 였다. 타입스크립트의 버전이 낮았던 이전에는 Type Alias의 기능이 많이 부족하여 Interface 사용을 지향했다고 한다. 하지만 버전업이 이뤄지면서 최신의 타입스크립트는 interface에서만 가능했던 기능들의 대부분이 type에서도 사용가능 하다고 한다.

그래서 여러가지를 찾아본 결과 둘 중에 어떤 것을 써야 한다라는 명확한 정답은 없다고 한다. 하지만 같은 상황에서는 동일한 방법으로 명명된 타입을 정의해 일관성을 유지해야 하기 때문에 둘의 공통점과 차이점을 비교해보면서 정리하면 좋을 것 같아 이 글을 쓰게 되었다.

그렇다면 이제 Type Aliases 와 Interfaces에 대해 알아보려고 한다.


Type Aliases


타입 별칭을 쓰는 이유 중에 가장 큰 이유는 재사용성에 있다라고 생각한다.

만약에 타입 별칭을 쓰지 않고 코드를 짠다면 아래와 같이 똑같은 타입을 또 사용하고 싶을 때 매번 선언해줘야 하는 상황이 생긴다. 이는 재사용성뿐만 아니라 코드의 가독성면에서도 많이 떨어지게 된다.

const person1 : {
	name: string,
	age: number
} = {
	name: 'Yujeong',
	age: 222
}

const person2 : {
	name: string,
	age: number
} = {
	name: 'Jade',
	age: 333
}

타입 별칭은 이러한 상황에 타입에 이름을 부여할 수 있게 해준다. 위에 코드를 타입 별칭을 사용하게 된다면 아래와 같이 바뀌게 된다.

type Person = {
	name: string;
	age: number;
};

const person1 : Person = {
	name: 'Yujeong',
	age: 222
}

const person2 : Person = {
	name: 'Jade',
	age: 333
}

또한 타입 별칭을 사용하면 객체 타입 뿐만 아니라 모든 타입에 대하여 이름을 부여할 수 있게 된다.

예를 들어, 어떤 매개변수의 형태로 string과 number 타입이 가능하다고 한다면 아래와 같이 Params라는 string과 number 타입이 가능한 유니언 타입에 대하여 타입 별칭을 부여할 수 있는 것이다.

type Params = string | number

function example(params: Params){
	// 무엇인가 처리중 🔥
}

Interface


인터페이스는 타입 별칭과 매우 유사한데 타입 선언 부분에서 타입 별칭과의 차이점은 객체에서만 사용이 가능하다는 점이다. (원시타입에 별칭을 부여하는 곳에서는 사용할 수 없다!)

즉, interface는 객체에 이름을 지정하는 또 다른 방법이다.

interface Person {
	name: string;
	age: number;
};

const person1 : Person = {
	name: 'Yujeong',
	age: 222
}

const person2 : Person = {
	name: 'Jade',
	age: 333
}

Type vs Interface


그렇다면 두가지에는 어떤 공통점과 차이점이 존재할까?

앞으로 알아볼 공통점과 차이점에서는 아래의 타입과 인터페이스를 사용하여 예시를 들어볼까 한다.

type RectangleType = {
  width: number;
  height: number;
};

interface RectangleInterface {
  width: number;
  height: number;
}

우리는 이런점에서 같아요


  1. 타입과 인터페이스 둘 다 object를 만들 수 있다.

    const rectangle1: RectangleType = {
      width: 10;
      height: 10;
    };
    
    const rectangle2: RectangleInterface = {
      width: 20;
      height: 20;
    };
  1. 타입과 인터페이스 둘 다 class에서 구현이 가능하다.

    class Rectangle1 implements RectangleType {
      width: number;
      height: number;
    };
    
    class Rectangle2 implements RectangleInterface {
      width: number;
      height: number;
    };
  1. 타입과 인터페이스 둘 다 제너릭이 가능하다.

    type ListType<T> = {
      first: T;
      second: T;
    };
    
    interface ListInterface<T> {
      first: T;
      second: T;
    }

우리는 이런점에서 달라요


  1. 타입과 인터페이스 둘 다 확장이 가능하나 확장하는 방법이 다르다.

    • interface는 상속을 이용해서 확장 가능하다.
      • 하지만 interface는 유니온 타입과 같은 복잡한 타입을 확장할 수 없다.
      • 복잡한 타입을 확장하고자 할 때는 타입과 extends 를 사용해야 한다.
    • type은 intersection (교집합)을 이용해서 확장 가능하다.
    type RectangleWithColorType = RectangleType & {color: string};
    
    interface RectangleWithColorInterface extends RectangleInterface {
      color: string;
    }
  1. 타입을 선언한 후에 interface에서는 선언 병합(declaration merging) 이 가능하다. (type에는 없는 interface만의 강력한 기능이다!)

    • 아래와 같이 기존의 인터페이스에 새 필드를 추가해서 사용할 수 있다.
    interface Person {
    	name: string;
    	age: number;
    };
    
    interface Person {
    	hobby: string;
    }
    
    // Person은 name, age, hobby를 모두 선언해줘야하는 타입이 되었다.
    const person: Person {
    	name: 'Yujeong',
    	age: 26,
    	hobby: 'Baking'
    }
  1. 1번에서 언급한 것처럼 타입은 유니온 타입은 존재하지만 유니온 인터페이스는 존재하지 않는다.

    type AB = 'A' | 'B'
    interface AB {
    	// ... ❌
    }
  1. 타입에서는 튜플과 배열 타입도 더 간결하게 표현할 수 있다.

    • 인터페이스에서도 비슷한 튜플을 구현할 수는 있지만 타입과는 다르게 튜플에서 사용할 수 있는 concat같은 메서드들은 사용할 수 없다.
    type Tuple = [number, number]
    type ArrayList = string[];
    
    // * ------------------------------------* //
    
    interface TupleLikeInterface {
    	0: number;
    	1: number;
    	length: 2;
    }
    
    const tuple: TupleLikeInterface = [10, 20] // ✅
  1. 타입에서는 computed properties을 사용한 타입 선언이 가능하다. (interface에서는 불가능하다.)

    type Person = {
      name: string;
      age: number;
    };
    
    // Person에 name이라는 키가 가진 type을 사용한다.
    // 즉 Name은 string 이라는 타입을 가지게된다.
    type Name = Person['name']; // string

어떤걸 언제 사용해야 할까요?


  • 아무래도 인터페이스는 복잡한 타입 선언이 불가능하기 때문에 복잡한 타입이라면 타입 별칭을 사용해야 한다.
  • 하지만 타입과 인터페이스 두 가지 방법으로 표현이 가능한 객체 타입이라면 일관성과 보강의 관점에서 고려해 봐야 한다.

불현듯이 드는 개인적인 생각으로 객체지향적인 프로그래밍을 할 때의 interface란 클래스에 구격 사양으로 사용되곤 하기 때문에 어떤 것을 구현할 목적이 아니라 값을 담아 둘 용도로 사용할 때에는 interface 보다는 type이 더 적절하지 않을까 하는 생각이든다.

하지만 제일 중요한 것은 현재 프로젝트에서 어떤 문법을 사용할지 결정할 때 일관된 스타일을 정하고 사용해야 하는 것 같다.


Reference


  • TypeScript Documentation
  • <이펙티브 타입스크립트> (댄 밴더캄 지음, 장원호 옮김, 인사이트 2021)
profile
언제나 새로운 도전을 꿈꾸는 개발자

5개의 댓글

comment-user-thumbnail
2022년 9월 15일

타입스크립트 공부하고 있는데 잘 봤습니다!

1개의 답글
comment-user-thumbnail
2022년 9월 15일

썸네일은 어떻게 만드나요..??

1개의 답글