By kimcoder
2022.01.03

Interfaces vs Type Aliases

타입스크립트에서는 아래 2가지 방식으로 타입 정의를 할 수 있다.

  • Interface
  • Type Aliases

타입 정의를 하고 추론하는데에 있어 어떤 방식을 사용해도 기능 구현에 문제가 없을테지만,
이 방식들이 어떤 특징을 가지고 있고 방식간 차이점이 무엇인지 알아보자.

Interfaces

  • Interface 선언은 Object Type의 이름을 지정하는 또 다른 방식이다.

    이는 Object Type으로 표현 가능한 타입에 대해서만 interface로의 선언이 가능하다는 것을 의미한다.

function track(point: { x: number; y: number }) {
  // ..
}

interface Point {
  x: number;
  y: number;
}

function track(point: Point) {
  // ..
}

위의 예제에서와 같이 { x: number, y: number }interface Point로 선언하여 대체할 수 있다.

Extending

  • interfaceextends 키워드를 통해 타입의 확장이 가능하다.
interface Point {
  x: number;
  y: number;
}

interface Point3D extends Point {
  z: number;
}

// 선언된 interface는 class가 구현해야 할 인터페이스 혹은 객체의 타입으로 사용이 가능하다.
class Pointer implements Point {
  x = 0;
  y = 1;

  //...
}

const pointB: Point3D = {
  x: 0,
  y: 1,
  z: 2,
};

function getPointer({ x, y }: Point) {
  return new Pointer({ x, y });
}

Declaration Merging

  • interface는 동일한 이름으로 추가 선언을 통해 병합이 가능하다.
    • 여기서 동일한 이름의 선언한 interface 갯수는 중요하지 않다. 모두 병합이 된다.
interface Point {
  x: number;
  y: number;
}

interface Point {
  z: number;
}

const pointA: Point = {
  x: 0,
  y: 1,
  z: 3,
};

이를 선언 병합(Declaration Merging)이라고 한다.

선언 병합은 interface 뿐만아니라 namespace, enum도 같은 방식으로 선언 병합이 된다.

namespace는 class, function, enum, interface, type aliasing과도 이름이 같다면 선언 병합이 된다.

interface Point {
  x: number;
  y: number;
  getDistance: (x: number) => number;
}

// Error: Property 'x' must be of type 'number';
interface Point {
  x: string;
}

선언 병합시, 같은 이름으로 되어있는 속성 중복된다면, 타입이 변해서는 안된다.
개방 폐쇄 원칙을 준수한 것이라고 생각해도 좋을 것 같다.

interface Point {
  x: number;
  y: number;
  equal(x: number, y: number): number;
}

interface Point {
  equal({ x, y }: { x: number; y: number }): number;
}

또한, 선언 병합시, 함수 오버로드도 위와 같이 가능하다.

Type Aliases

  • Type Aliasing은 특정 타입에 대해 다른 이름을 지어주는 것이다.
  • 타입 종류에 대한 제약은 없다.
type Point = {
  x: number;
  y: number;
};

type Name = string;

type ID = string | number;

type SomeFunction = () => any;

위와 같이 string과 같이 자바스크립트의 원시타입에 대해 별칭을 할 수 있으며, Object Type, Union Type에 대해서도 별칭이 가능하다.

Union Type은 2가지 이상의 다른 타입으로 구성된 타입을 말한다.

  • Type Aliasing은 단지 다른 이름일 뿐이다.
type UserInputSanitizedString = string;

function sanitizeInput(str: string): UserInputSanitizedString {
  return sanitize(str);
}

let userInput = sanitizeInput(getInput());

// 문자열 할당
userInput = 'new input';

UserInputSanitizedStringstring의 다른 이름이기 때문에 결과적으로는 string과 동일한 타입으로 추론이 되어,
위와 같이 UserInputSanitizedString의 타입으로 추론되는 변수에 다른 문자열의 할당이 가능하다.

Extending

  • Type Aliasingintersection을 통해 타입의 확장이 가능하다.
type Animal = {
  name: string;
};

type Bear = Animal & {
  honey: boolean;
};

const bear = getBear();
bear.name;
bear.honey;
  • Type Aliasing은 선언 병합(Declaration Merging)이 되지 않는다.
    • 따라서, 동일한 이름의 Type에 새로운 속성을 추가 할 수 없다.
    • 이는 interface와 가장 큰 차이라고 볼 수 있다.
type Window = {
  title: string;
};

type Window = {
  ts: TypeScriptAPI;
};
// Error: Duplicate identifier 'Window'.

Use with extends, implements

  • Type Aliasing은 Object Type일 경우에 Extends, implements 키워드와도 같이 사용이 가능하다.
type Point = {
  x: number;
  y: number;
};

interface Point3D extends Point {
  z: number;
}

class Pointer implements Point {
  x = 0;
  y = 1;

  //...
}

마치며

타입 정의하는데 2가지의 방법과 각 특징들에 대해 알아보았다.
가장 주된 차이점은 어떤 타입을 정의할 수 있는가선언 병합이 되는가라고 생각한다.
타입스크립트 공식 문서에는 둘 중 하나의 선택은 개인의 취향이고, 직접적인 경험을 통해 판단하고자 한다면, type의 기능에 대한 필요성을 느끼기 전까지 먼저 interface를 사용하라고 한다.

References