[typescript] enum vs union

김지원·2022년 5월 22일

typescript

목록 보기
1/3
post-thumbnail

이 글을 적게 된 이유

type userStatus = 'subscribe' | 'buy' | 'cancel';

typescript로 개발을 하다보면 위처럼 union 타입이 필요한 경우가 있다.
상태값 같은 여러 경우가 있는 방식이면 가독성이 떨어질 수 있다.

그래서 typescript에서는 2.4버전부터 enum(열거형 데이터 유형)을 사용할 수 있게 하였다.

enum

enum useStatus {
  subscribe: 'subscribe',
  buy: 'buy',
  cancel: 'cancel',
}

이렇게 가독성을 높이고 열거형만의 장점들이 있어서 애용하고 있는 코드 구성 방법이였다.

장점에는

  • javascript의 인라인 코드로 런타임 및 컴파일 시간을 절약할 수 있다.
  • 이 코드를 사용하는 의도와 사례를 확실하게 표현할 수 있다.
  • 숫자 열거형에서는 value값을 자동으로 할당시켜주는 기능이 있다.
  • union 유형에 대해 반복이 가능하다!
enum userStatus {
  first = 1,
  second,
}

console.log(userStatus.second); // 2

하지만 enum을 사용하면서 불편한 점도 존재한다.

enum의 문제점

1. Tree-shacking

Tree-shacking을 간단하게 설명하자면 컴파일시 사용하지 않는 코드를 삭제하는 기능이다.
모듈화를 했지만 어디서도 참조하지 않는 코드들을 삭제해서 번들 크기를 줄이며 시간을 단축해준다. (ts -> js 컴파일 시)

하지만 enum은 사용하지 않아도 코드가 삭제되지 않는다.😨
이 이유는 enum코드를 타입스크립트 컴파일이 취급하는 방식에 나와있다.

타입스크립트 컴파일은 enum을 즉시 실행 함수로 사용하게 된다. (자바스크립트에는 열거형이 없기 때문에)
그러니 사용하지 않는 코드로 판단하지 않게 된다.

코드의 양이 많아지고 enum이 많아질수록 이 경우는 큰 단점으로 나오게 될 것이다!

2. 숫자 값에 대한 타입 체크가 어렵다..

enum NumberMember {
  first = 1,
  second,
}

// no error
const numberExample: NumberMember = 5;

enum StringMember {
  first = "first",
  second = "second",
}

// error: Type ""third""is not assignalbe to type 'StringMember'
const stringExample: StringMember = 'third';

위에 예시를 보면 숫자 열거형에는 할당되어있지 않은 값이여도 에러없이 변수에 잘 할당되는 것을 볼 수 있다.

3. 코드 상에서 불편한 점

사실 위의 문자열 예시는 잘못된 예시다.

enum StringMember {
  first = "first",
  second = "second",
}

const stringExample: StringMember = StringMember.first;

value에 해당하는 문자열이 아니라 enum에서 할당해서 key를 가져와 value값을 받아와야된다.
확실하긴 하지만 사용하는 입장에서는 살짝 불편하기도 했다.
필자가 생각하기엔 js에서 컴파일할 때 즉시실행함수로 사용하기 때문에 이런 식으로 타입 에러를 띄우는 것 같다.

실수로 key와 value값이 다르게 써져있을때도 에러찾기 힘들 것 같다.

enum말고 열거형을 나타내는 방법

먼저 공식문서를 찾아보았다.

역시나 공식 문서에도 object와 enum을 비교하는 글이 있었고 해결방안이 있었다.

객체나 배열을 type assertion을 이용해 const로 선언하고 타입스크립트 문법을 이용해 나타내는 방법이다.

const stringMember = {
 	first: 'first',
    second: 'second'
} as const;

type StringMember = typeof stringMember[keyof typeof stringMember];
  • 일단 as const를 함으로써 원시 값의 타입처럼 리터럴 타입의 추론 범위를 줄이고 값의 재할당을 막는다. === readonly역할

  • keyof로 Object key들의 리터럴 값들을 가져온다.

  • 여기서 불편한 점은 똑같은 코드를 두 번 쓴다는 점이다.

제네릭을 이용해 바꿔보자

const Union<T> = typeof<T>[keyof <T>];
type StringMember = Union<typeof stringMember>;
//역시 제네릭은 마법😁

참고 문헌

https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums
https://engineering.linecorp.com/ko/blog/typescript-enum-tree-shaking/
https://blog.logrocket.com/writing-readable-code-with-typescript-enums-a84864f340e9/
https://www.becomebetterprogrammer.com/typescript-union-types-vs-enums/

profile
backend-developer

1개의 댓글

comment-user-thumbnail
2022년 5월 29일

글 잘 읽고 있습니다~

답글 달기