icon
2장 : 타입스크립트 기본 타입

타입 별칭


우리는 객체의 '모양'을 정의할 때 interface를 사용하는 방법을 배웠습니다. interface는 주로 객체의 타입을 정의하고, 확장하거나 구현할 때 강력한 도구입니다. 하지만 타입스크립트에는 이 외에도 복잡한 타입을 간결하게 재사용할 수 있도록 돕는 또 다른 강력한 기능이 있는데, 바로 타입 별칭(Type Aliases) 입니다.

타입 별칭은 기존에 존재하는 타입에 새로운 이름(별칭) 을 부여하는 방법입니다. 이를 통해 복잡한 유니온 타입, 인터섹션 타입, 혹은 객체 리터럴 타입을 더 읽기 쉽고 관리하기 쉬운 이름으로 만들 수 있습니다. 마치 긴 주소를 짧은 별명으로 부르는 것과 같습니다.


타입 별칭 정의하기

타입 별칭은 type 키워드를 사용하여 정의합니다.

// string 타입에 'Name'이라는 별칭을 부여합니다.
type Name = string;
let myName: Name = "김타입";

// number 타입에 'Age'라는 별칭을 부여합니다.
type Age = number;
let myAge: Age = 30;

// 유니온 타입에 별칭을 부여합니다.
type Id = string | number;
let userId: Id = "user_007";
let adminId: Id = 1004;

// 인터섹션 타입에 별칭을 부여합니다.
type Point = { x: number; y: number };
type ZCoordinate = { z: number };
type ThreeDPoint = Point & ZCoordinate;

let point3d: ThreeDPoint = { x: 10, y: 20, z: 30 };

console.log(myName);     // 김타입
console.log(userId);     // user_007
console.log(point3d.z);  // 30

위 예시에서 볼 수 있듯이, type 키워드 뒤에 별칭으로 사용할 이름을 쓰고, 등호(=) 뒤에 별칭을 부여할 타입을 정의합니다. 이렇게 정의된 별칭은 원래의 타입을 사용하는 것처럼 어디든 사용할 수 있습니다.


객체 타입 정의에 타입 별칭 사용하기

타입 별칭은 특히 객체 타입을 정의할 때 매우 유용합니다. interface와 유사하게 객체의 모양을 정의할 수 있습니다.

// 'User'라는 타입 별칭으로 객체 타입을 정의합니다.
type User = {
  id: string;
  name: string;
  email?: string; // 선택적 속성
  readonly registrationDate: Date; // 읽기 전용 속성
};

let currentUser: User = {
  id: "u_abc",
  name: "이코딩",
  registrationDate: new Date(),
};

let newUser: User = {
  id: "u_xyz",
  name: "박개발",
  email: "park@example.com",
  registrationDate: new Date("2024-01-01"),
};

console.log(currentUser.name); // 이코딩
// newUser.registrationDate = new Date(); // Error: registrationDate는 읽기 전용입니다.

이전 2.3절에서 interface로 정의했던 Person과 거의 동일하게 User 타입을 정의했음을 알 수 있습니다.


인터페이스와 타입 별칭의 차이점

타입 별칭과 인터페이스는 객체의 타입을 정의하는 데 모두 사용될 수 있어 비슷하게 느껴질 수 있습니다. 하지만 몇 가지 중요한 차이점이 있습니다.

  1. 확장 및 구현 방식

    • 인터페이스: extends 키워드를 사용하여 다른 인터페이스를 확장할 수 있고, 클래스가 implements 키워드를 사용하여 인터페이스를 구현할 수 있습니다. 이는 객체 지향적인 상속과 계약 관계를 명확히 표현할 때 유리합니다.
    • 타입 별칭: extendsimplements 키워드를 직접 사용할 수는 없습니다. 하지만 인터섹션 타입(&) 을 사용하여 인터페이스와 유사한 '확장' 효과를 낼 수 있습니다.
    // 인터페이스 확장 (extends)
    interface BaseShape {
      color: string;
    }
    interface Circle extends BaseShape {
      radius: number;
    }
    
    // 타입 별칭을 이용한 '확장' (인터섹션 &)
    type BaseShapeAlias = {
      color: string;
    };
    type CircleAlias = BaseShapeAlias & {
      radius: number;
    };
  2. 재선언 (Declaration Merging)

    • 인터페이스: 동일한 이름으로 여러 번 선언해도 자동으로 병합(Merge)됩니다. 이는 라이브러리에서 기존 타입에 새로운 속성을 추가하는 등의 상황에서 유용합니다.
    • 타입 별칭: 동일한 이름으로 여러 번 선언할 수 없습니다. 즉, 재선언 시 오류가 발생합니다.
    // 인터페이스 병합 예시
    interface Car {
      brand: string;
    }
    interface Car { // 동일한 이름으로 다시 선언해도 오류 없음
      model: string;
    }
    let myCar: Car = { brand: "BMW", model: "X5" }; // brand와 model 모두 가짐
    
    // 타입 별칭 재선언 시 오류
    // type Product = { name: string };
    // type Product = { price: number }; // Error: Duplicate identifier 'Product'.
  3. 원시 타입, 유니온, 튜플 등 정의

    • 타입 별칭: 원시 타입, 유니온, 튜플 등 모든 종류의 타입에 별칭을 부여할 수 있습니다.
    • 인터페이스: 주로 객체의 모양을 정의하는 데 사용되며, 원시 타입이나 유니온, 튜플을 직접적으로 정의할 수는 없습니다. (물론 인터페이스 내부 속성으로는 가능합니다.)
    type StringOrNumber = string | number; // 타입 별칭은 유니온 타입에 사용 가능
    type PointTuple = [number, number];   // 타입 별칭은 튜플 타입에 사용 가능
    // interface MyPrimitive extends string {} // Error: 인터페이스는 원시 타입을 확장할 수 없습니다.

언제 무엇을 사용해야 할까?

일반적인 가이드라인은 다음과 같습니다.

  • 객체의 '모양(Shape)'을 정의하고, 향후 다른 인터페이스가 이를 확장하거나 클래스가 구현해야 할 가능성이 있다면 interface를 사용하는 것이 좋습니다. 특히 라이브러리나 프레임워크처럼 확장성이 중요한 설계에서는 interface가 더 강력한 도구입니다.
  • 원시 타입, 유니온 타입, 튜플 타입 등 객체 이외의 타입을 포함하여 어떤 타입이든 간결하게 별칭을 부여하고 싶을 때는 type 별칭을 사용합니다. interface로는 표현하기 어려운 복잡한 조합의 타입을 정의할 때 유용합니다.

최근 타입스크립트 버전에서는 interfacetype 별칭의 기능적 차이가 많이 줄어들었지만, 위에서 언급한 주요 차이점들은 여전히 유효합니다. 프로젝트의 성격과 팀의 컨벤션에 따라 선택하여 사용할 수 있습니다.


이로써 타입스크립트의 기본 타입들에 대한 학습을 마쳤습니다. 숫자, 문자열, 불리언과 같은 기본 데이터부터 시작하여, 복잡한 객체와 그 형태를 정의하는 인터페이스, 그리고 유연한 타입 조합을 위한 유니온/인터섹션, 마지막으로 타입에 새 이름을 부여하는 타입 별칭까지, 타입스크립트의 견고한 타입 시스템을 위한 핵심 개념들을 살펴보았습니다.