Skip to content

5. 연산자를 이용한 타입 정의


1. 유니언(Union) 타입

  • 유니언 타입이란 자바스크립트의 ||(OR 연산자)와 같은 의미의 타입이다.


function logText(text: string | number) {
  // ...
}


  • 위 함수의 파라미터 text에는 문자열 타입이나 숫자 타입 모두 올 수 있다.
  • 이처럼 | 연산자를 이용하여 타입을 여러 개 연결하는 방식을 유니언 타입 정의 방식이라고 부른다.


2. 유니언 타입의 장점

  • 유니언 타입의 장점은 다음 코드를 비교하면 바로 알 수 있다.


// any를 사용하는 경우
function getAge(age: any) {
  // error. age의 타입이 `any`로 추론되기 때문에 숫자 관련된 API를 작성할 때 코드가 자동 완성되지 않는다.
  age.toFixe();
  return age;
}

// 유니언 타입을 사용하는 경우
function getAge(age: number | string) {
  if (typeof age === "number") {
    // 정상 동작. age의 타입이 `number`로 추론되기 때문에 숫자 관련된 API를 쉽게 자동완성 할 수 있다.
    age.toFixed();
    return age;
  }

  if (typeof age === "string") {
    return age;
  }

  return new TypeError("age must be number or string");
}


  • 이처럼 any를 사용하는 경우 마치 자바스크립트로 작성하는 것처럼 동작을 하고 유니언 타입을 사용하면 타입스크립트의 이점을 살리면서 코딩할 수 있다.


3. 인터섹션(Intersection) 타입

  • 인터섹션 타입은 여러 타입을 모두 만족하는 하나의 타입을 의미한다.


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

interface Developer {
  name: string;
  skill: number;
}

type Capt = Person & Developer;


  • 위 코드는 Person 인터페이스의 타입 정의와 Developer 인터페이스의 타입 정의를 & 연산자를 이용하여 합친 후 Capt라는 타입에 할당한 코드이다.
  • 결과적으로 Capt의 타입은 다음과 같이 정의된다.


{
  name: string;
  age: number;
  skill: string;
}


  • 이처럼 & 연산자를 이용해 여러 개의 타입 정의를 하나로 합치는 방식을 인터섹션 타입 정의 방식이라고 한다.


4. 유니언 타입을 쓸 때 주의할 점

  • 앞에서 유니언 타입과 인터섹션 타입을 살펴봤다.
  • 인터페이스와 같은 타입을 다룰 때는 다음과 같은 논리적 사고를 주의해야 한다.


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

interface Developer {
  name: string;
  skill: number;
}

function introduce(someone: Person | Developer) {
  someone.name; // 정상 동작
  someone.age; // 타입 오류
  someone.skill; // 타입 오류
}


  • 여기서 introduce() 함수의 파라미터 타입을 Person, Developer 인터페이스의 유니언 타입으로 정의하였다.
  • 유니언 타입은 A도 될 수 있고 B도 될 수 있는 타입이라고 생각하면, 파라미터 타입이 Person도 되고 Developer도 되기 때문에 함수 안에서 당연히 이 인터페이스들이 제공하는 속성들인 ageskill을 사용할 수 있다고 생각할 수 있다.
  • 하지만, 타입스크립트 관점에서는 introduce() 함수를 호출하는 시점에 Person 타입이 올지 Developer 타입이 올지 알 수 가 없기 때문에 어느 타입이 들어오든 간에 오류가 안 나는 방향으로 타입을 추론하게 된다.


const capt: Person = { name: "capt", age: 100 };

// 만약 `introduce` 함수 안에서 `someone.skill` 속성을 접근하고 있으면 함수에서 오류 발생
introduce(capt);
const tony: Developer = { name: "tony", skill: "iron making" };

// 만약 `introduce` 함수 안에서 `someone.age` 속성을 접근하고 있으면 함수에서 오류 발생
introduce(tony);


  • 결과적으로 introduce() 함수 안에서는 별도의 타입 가드(Type Guard)를 이용하여 타입의 범위를 좁히지 않는 이상 기본적으로는 PersonDeveloper 두 타입에 공통적으로 들어있는 속성인 name만 접근할 수 있게 된다.


function introduce(someone: Person | Developer) {
  console.log(someone.name); // O 정상 동작
}

References