Skip to content

유니언 타입 (Union Types)

TypeScript에서 유니언 타입은 하나의 값이 여러 타입 중 하나일 수 있음을 표현한다.
집합 이론에서의 합집합(A ∪ B) 개념과 동일하며, | 기호를 사용한다.

🧩 대수 타입(Algebraic type)이란?

대수 타입은 여러 개의 타입을 조합해서 새로운 타입을 만드는 방식을 말한다.
TypeScript에서는 대표적으로 Union TypeIntersection Type이 있다.

  • Union Type: 여러 타입 중 하나를 허용하는 타입
  • Intersection Type: 여러 타입을 모두 만족해야 하는 타입

1. 기본 사용법

유니언 타입을 정의할 때는 파이프 | 기호를 사용해 여러 타입을 나열한다.

ts
let userId: string | number;

userId = 1;
userId = '1';

userId = true; // Error
userId = {}; // Error

userIdstring 또는 number 타입만 허용한다.
따라서 boolean이나 객체는 할당할 수 없다.


함수의 매개변수에도 유니언 타입을 사용할 수 있다.

ts
// 함수
function printUserId(id: string | number) {
  console.log(id);
}

printUserId('1');
printUserId(1);

printUserId({}); // Error

2. Type Narrowing (타입 좁히기)

유니언 타입은 여러 타입이 가능하기 때문에, 값을 사용할 때는 실제 타입을 확인해야 하는 경우가 많다.
이처럼 조건문 등을 사용해 타입을 더 구체적으로 좁히는 것을 타입 좁히기(Type Narrowing)라고 한다.

ts
function processValue(value: number | string) {
  if (typeof value === 'string') {
    // 문자열인 경우
    return value.toUpperCase();
  }

  // 숫자인 경우
  return value.toString().toUpperCase();
}
  • typeof: 런타임에서 타입 체크
  • toString(): 숫자를 문자열로 변환
  • toUpperCase(): 문자열 메서드

value는 처음에는 number | string 타입이다.
하지만 typeof value === 'string' 조건문 안에서는 TypeScript가 valuestring 타입으로 판단한다.

그래서 문자열 메서드인 toUpperCase()를 사용할 수 있다.


3. 배열에 유니언 타입 적용

유니언 타입은 배열 요소에도 적용할 수 있다.

ts
let mixedValues: (string | number)[] = [];

mixedValues.push('100');
mixedValues.push(100);

mixedValues.push(true); // Error
mixedValues.push([]); // Error

(string | number)[]는 배열의 각 요소가 string 또는 number일 수 있다는 뜻이다.

비슷해 보이지만 아래 두 타입은 의미가 다르다.

ts
let mixedArray: (string | number)[] = ['hello', 1, 'world', 2];

let stringOrNumberArray: string[] | number[] = ['hello', 'world'];
  • (string | number)[]: 문자열과 숫자가 섞인 배열
  • string[] | number[]: 문자열 배열이거나 숫자 배열

ts
let arr1: (string | number)[] = ['a', 1, 'b', 2]; // 가능

let arr2: string[] | number[] = ['a', 'b']; // 가능
let arr3: string[] | number[] = [1, 2]; // 가능
let arr4: string[] | number[] = ['a', 1]; // Error

4. 리터럴 타입과 유니언 타입

리터럴 타입은 특정 값만 허용하는 타입이다.
유니언 타입과 함께 사용하면 정해진 값 중 하나만 선택하도록 제한할 수 있다.

ts
const toggle = (option: 'on' | 'off') => {
  console.log(option);
};

toggle('on');
toggle('off');

toggle(true); // Error
toggle(0); // Error

예를 들어 티셔츠 사이즈처럼 정해진 값만 허용해야 하는 경우에 사용할 수 있다.

ts
// 예시: 티셔츠 사이즈 타입
type Size = 'xs' | 's' | 'm' | 'l' | 'xl';

let tShirtSize: Size;

tShirtSize = 'm';
tShirtSize = 'xxl'; // Error

function setSize(size: Size) {
  switch (size) {
    case 'xs':
      console.log('아주 작음');
      break;
    case 's':
      console.log('작음');
      break;
    case 'm':
      console.log('보통');
      break;
    case 'l':
      console.log('큼');
      break;
    case 'xl':
      console.log('아주 큼');
      break;
  }
}

setSize('m');
setSize('xxl'); // Error

5. 타입 별칭 (Type Aliases)

유니언 타입을 자주 쓰는 경우, 타입 별칭을 만들어 재사용할 수 있다.

ts
type SuccessCode = 200 | 201 | 202;
type ErrorCode = 500 | 501 | 503;

let responseCode: SuccessCode | ErrorCode;

responseCode = 200; // 가능
responseCode = 503; // 가능

responseCode = 404; // Error

문자열 유니언 타입도 자주 사용된다.

ts
type Status = 'idle' | 'loading' | 'success' | 'error';

let status: Status;

status = 'loading';
status = 'success';

status = 'pending'; // Error

6. 객체에 유니언 타입 적용하기

객체 타입에도 유니언 타입을 사용할 수 있다.

ts
type Dog = {
  name: string;
  color: string;
};

type Person = {
  name: string;
  language: string;
};

type DogOrPerson = Dog | Person;

DogOrPerson 타입은 Dog 타입이거나 Person 타입일 수 있다.

ts
let dog: DogOrPerson = {
  name: '돌돌이',
  color: 'brown',
};

let person: DogOrPerson = {
  name: 'Binny',
  language: 'TypeScript',
};

Dog의 구조를 만족하거나 Person의 구조를 만족하면 할당할 수 있다.


공통 프로퍼티만 있는 경우

ts
let value: DogOrPerson = {
  name: 'unknown',
}; // Error

Dog 타입이 되려면 color가 필요하고, Person 타입이 되려면 language가 필요하다.
하지만 위 객체는 name만 가지고 있으므로 두 타입 중 어느 쪽도 만족하지 못한다.


두 타입의 프로퍼티를 모두 가진 경우

ts
let value: DogOrPerson = {
  name: '초코',
  color: 'brown',
  language: 'Korean',
};

위 객체는 Dog 타입이 요구하는 name, color를 가지고 있고,
Person 타입이 요구하는 name, language도 가지고 있다.

따라서 Dog | Person 타입에 할당할 수 있다.


6-1. 객체 유니언 타입 사용 시 주의점

객체 유니언 타입의 값을 사용할 때는 공통 프로퍼티에만 바로 접근할 수 있다.

ts
function printInfo(value: Dog | Person) {
  console.log(value.name); // 가능

  console.log(value.color); // Error
  console.log(value.language); // Error
}

nameDogPerson에 모두 존재하는 공통 프로퍼티이므로 바로 접근할 수 있다.
하지만 colorDog에만 있고, languagePerson에만 있기 때문에 바로 접근할 수 없다.

이럴 때는 타입 좁히기를 사용한다.

ts
function printInfo(value: Dog | Person) {
  console.log(value.name);

  if ('color' in value) {
    console.log(value.color);
  }

  if ('language' in value) {
    console.log(value.language);
  }
}

in 연산자를 사용하면 특정 프로퍼티가 있는지 확인하면서 타입을 좁힐 수 있다.


👩🏻‍💻 요약

  • 유니언 타입은 여러 타입 중 하나를 허용하는 타입이다.
  • | 기호를 사용해 타입을 연결한다.
  • 유니언 타입은 함수 매개변수, 배열, 리터럴 타입, 객체 타입 등에 사용할 수 있다.
  • 유니언 타입의 값을 사용할 때는 타입 좁히기가 필요한 경우가 많다.
  • 객체 유니언 타입에서는 공통 프로퍼티만 바로 접근할 수 있다.
  • 특정 프로퍼티에 접근하려면 typeof, in 같은 방법으로 타입을 좁혀야 한다.