[TS] 서로소 유니온 타입

TIL

서로소 유니온타입은 교집합이 없는 타입들 즉, 서로소 관계에 있는 타입들을 모아 만든 유니온 타입을 말한다.

사례 1: 회원 역할 분류

type Admin = {
  name: string;
  kickCount: number;
};

type Member = {
  name: string;
  point: number;
};

type Guest = {
  name: string;
  visitCount: number;
};

type User = Admin | Member | Guest;

function login(user: User) {
  if ("kickCount" in user) {
    // Admin
    console.log(`${user.name}님 현재까지 ${user.kickCount}명 추방했습니다`);
  } else if ("point" in user) {
    // Member
    console.log(`${user.name}님 현재까지 ${user.point}모았습니다`);
  } else {
    // Guest
    console.log(`${user.name}님 현재까지 ${user.visitCount}번 오셨습니다`);
  }
}

위 코드의 문제점은 "kickCount" in user와 같은 조건식만으로는 어떤 역할(Admin/Member/Guest)인지 직관적으로 파악하기 어렵다는 것이다.

 

다음과 같이 각 타입에 String Literal타입의 tag 프로퍼티를 추가하면, login함수의 타입가드를 더 직관적으로 수정할 수 있게 된다.

type Admin = {
  tag: "ADMIN";
  name: string;
  kickCount: number;
};

type Member = {
  tag: "MEMBER";
  name: string;
  point: number;
};

type Guest = {
  tag: "GUEST";
  name: string;
  visitCount: number;
};

이제 Admin, Member, Guest 타입은 각각 고유한 tag값을 가지므로 서로소 관계가 된다. 즉, 세 타입 사이에 교집합이 없다.

function login(user: User) {
  if (user.tag === "ADMIN") {
    // Admin
    console.log(`${user.name}님 현재까지 ${user.kickCount}명 추방했습니다`);
  } else if (user.tag === "MEMBER") {
    // Member
    console.log(`${user.name}님 현재까지 ${user.point}모았습니다`);
  } else {
    // Guest
    console.log(`${user.name}님 현재까지 ${user.visitCount}번 오셨습니다`);
  }
}

user.tag를 확인하는 것만으로도 어떤 타입인지 명확하게 알 수 있고, 타입스크립트도 각 분기에서 타입을 정확하게 좁혀준다.

 

또는 switch를 이용해 더 직관적으로 변경할 수도 있다.

switch (user.tag) {
    case "ADMIN": {
      console.log(`${user.name}님 현재까지 ${user.kickCount}명 추방했습니다`);
      break;
    }
    case "MEMBER": {
      console.log(`${user.name}님 현재까지 ${user.point}모았습니다`);
      break;
    }
    case "GUEST": {
      console.log(`${user.name}님 현재까지 ${user.visitCount}번 오셨습니다`);
      break;
    }
  }

switch 문을 사용하면 각 case마다 타입이 자동으로 좁혀지므로 더욱 안전하고 가독성 좋은 코드가 된다.

 

사례 2: 비동기 작업 상태 관리

비동기 작업의 상태를 표현하는 타입도 서로소 유니온으로 설계할 수 있다.

type LoadingTask = {
  state: "LOADING";
};
type FailedTask = {
  state: "FAILED";
  error: {
    message: string;
  };
};
type SuccessTask = {
  state: "SUCCESS";
  response: {
    data: string;
  };
};

type AsyncTask = LoadingTask | FailedTask | SuccessTask;
function processResult(task: AsyncTask) {
  switch (task.state) {
    case "LOADING": {
      console.log("로딩 중");
      break;
    }
    case "FAILED": {
      console.log(`에러 발생 : ${task.error.message}`);
      break;
    }
    case "SUCCESS": {
      console.log(`성공 : ${task.response.data}`);
      break;
    }
  }
}

state값에 따라 각 분기에서 타입이 좁혀지므로, task.error와 task.response를 안전하게 접근할 수 있다.

const loading: AsyncTask = {
  state: "LOADING",
};

const failed: AsyncTask = {
  state: "FAILED",
  error: {
    message: "오류 발생 원인은 ~~",
  },
};

const success: AsyncTask = {
  state: "SUCCESS",
  response: {
    data: "데이터 ~~",
  },
};

각 객체는 state값에 따라 필요한 프로퍼티만 가지므로, 잘못된 조합(예: "LOADING"상태인데 error 프로퍼티가 있는 경우)을 타입 수준에서 방지할 수 있다.

'TIL' 카테고리의 다른 글

[TS] 함수의 타입 정의  (0) 2025.11.01
[TS] 타입 단언 및 서로소 유니온 사용 연습  (0) 2025.11.01
[TS] 타입 좁히기  (0) 2025.10.31
[TS] 타입 단언  (0) 2025.10.31
웹페이지를 조작하는 DOM  (0) 2025.10.31
'TIL' 카테고리의 다른 글
  • [TS] 함수의 타입 정의
  • [TS] 타입 단언 및 서로소 유니온 사용 연습
  • [TS] 타입 좁히기
  • [TS] 타입 단언
고견
고견
개발 자국 남기기
  • 고견
    개발자국
    고견
  • 전체
    오늘
    어제
    • 분류 전체보기 (157)
      • Frontend (29)
        • Next.js (16)
        • JavaScript (7)
      • CS (19)
        • 자료구조 (9)
        • 알고리즘 (5)
        • 운영체제 (4)
        • 네트워크 (1)
      • TIL (93)
      • Dev Log (16)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    문자열
    App Router
    Spa
    CS
    generic
    useState
    자료구조
    제네릭
    인터페이스
    배열
    페이지 라우터
    emotion diary
    C
    클래스
    앱 라우터
    바닐라 자바스크립트
    algorithm
    함수 타입
    cs50
    Next.js
    알고리즘
    react
    타입 좁히기
    memory
    typescript
    Trouble Shooting
    트러블 슈팅
    javascript
    ai 감성 일기장
    Pages Router
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
고견
[TS] 서로소 유니온 타입
상단으로

티스토리툴바