typescript enum 을 union으로 변경하기

이하은·2021년 12월 1일
10

TypeScript

목록 보기
1/2

배경

type Status = 'todo' | 'inProgress' | 'complete' | 'cancel';

개발하다보면 위와 같은 union 타입이 종종 필요하다. 하지만 위와 같은 작성 방식이면 가짓수가 많아질때 가독성이 떨어진다.
enum 은 타입을 작성하는 타입코드 위치에도 사용할 수 있지만, 로직을 작성하는 값의 위치에도 사용할 수 있어서 사용성이 더 높게 느껴졌다. 그래서 아래와 같이 enum을 사용했었다.

enum

enum Status {
  todo = 'todo',
  inProgress = 'inProgress',
  complete = 'complete',
  cancel = 'cancel',
}

enum 사용의 문제 1
하지만 enum 을 사용할때는 Tree-shacking이 되지 않는 문제가 생긴다.
여기서 Tree-shaking이란 사용하지 않는 코드를 삭제하는 기능이다. Tree-shaking을 사용하면 export했지만 아무 데서도 import 하지 않은 모듈과 같은 사용하지 않는 코드를 삭제해서 번들 크기를 줄여 페이지가 표시되는 시간을 단축할 수 있다.

enum 코드를 사용하면 타입스크립트 컴파일러에서 자바스크립트에 없는 문법을 구현하기 위해 즉시 실행 함수를 사용하는데 이 즉시 실행 함수를 번들러에서 사용하지 않는 코드라고 판단할 수 없어서 Tree-shaking이 일어나지 않는다.

enum 사용의 문제 2
또 나중에 코드 상에서 사용할때에도 불편한 점이 있었는데, status = 'todo' 이렇게 enum value에 해당하는 문자열을 할당하려고 하면 Status 타입에 해당하지 않는다면서 에러를 띄운다. 그래서 status = Status.todo 이렇게 작성해줘야 해결이 된다.
이런식으로 enum의 value에 해당하는 string 값과 enum 타입이 일치하지 않다고 판단하는 타입에러가 종종 있었다.

enum 사용의 문제 3
enum 은 숫자 값에 대한 타입 체킹을 할 수 없다. 아래 사진에서 보다 싶이 100은 0~6 사이의 값이 아닌데 할당이 허용된다.

union

위 문제를 해결하기 위해 검색을 해보니 아래와 같은 해결책이 있었다.
객체 혹은 배열을 as const 로 선언한 후 타입스크립트의 문법으로 union 타입을 추출해 사용하는 방식이다.
참고로 타입스크립트 공식 문서에서도 객체에 as const 를 사용하여 enum을 대신 하는 방법을 소개하고 있다.

객체

const status = {
  todo: 'todo',
  inProgress: 'inProgress',
  complete: 'complete',
  cancel: 'cancel',
} as const;

type Status = typeof status[keyof typeof status];
// 'todo' | 'inProgress' | 'complete' | 'cancel'

배열

const status = ['todo', 'inProgress', 'complete', 'cancel'] as const;

type Status = typeof status[keyof typeof status];
// 'todo' | 'inProgress' | 'complete' | 'cancel'

위에서 사용한 타입스크립트 문법을 부연설명 하자면,

keyof는 Object의 key들의 lieteral 값들을 가져오는 키워드이다.

as const는 객체나 배열도 const로 선언한 원시 값의 타입처럼, "리터럴 타입의 추론 범위를 줄이고 값의 재할당을 막아준다(readonly)".
위 코드에서 as const를 사용하지 않으면 object의 value 값이 string으로 추론되지만 사용하면 'todo', 'inProgress'... 처럼 각각의 명시된 문자열 자체로 타입이 지정된다.

이제 위와 같은 방법으로 union 타입을 사용하면 tree shacking 의 문제도 해결이 되고, status = 'todo' 이렇게 할당도 문제없이 된다.

union 추출코드를 제네릭으로 추상화하기

제네릭을 통해 추상화하여 타입도 함수처럼 재사용할 수 있다.
union 추출이 필요할 때마다 매번 문법을 생각해내서 일일히 적기 귀찮으니 Union이라는 별도의 타입을 만들었다. 이제 Union 타입을 export 해서 필요할 때마다 재사용하면 편리하다.

// 이전 코드
// type Status = typeof status[keyof typeof status];

// 제네릭으로 추상화 한 코드
type Union<T> = T[keyof T];

// 사용 예시
type Status = Union<typeof status>;

예외

모바일 환경에서 안드로이드, ios 네이티브 환경과 호환을 해야하는 경우에는 enum 타입을 사용하는것이 네이티브 언어와 더 호환성이 좋다고 한다.

참고 링크

타입스크립트 핸드북 enum
LINE Engineering - TypeScript enum을 사용하지 않는 게 좋은 이유를 Tree-shaking 관점에서 소개합니다
enum type 대신 union type으로 변경하기
Enum vs as const

profile
완벽함보단 꾸준함으로

1개의 댓글

comment-user-thumbnail
2022년 4월 11일

안녕하세요^^ 좋은 글 잘 읽었습니다.
글을 읽고 궁금증이 생겨 댓글을 남깁니다.
예외에서 enum이 네이티브 언어와 호환성이 좋다고 하셨는데 그 이유는 무엇인가요??

답글 달기