TypeScript 3.6 enum 대신 union type 사용하기

문제 상황

React 프로젝트에 TypeScript를 도입하면서 상태 값 관리를 위해 enum을 사용했다.

enum UserRole {
  Admin = 'ADMIN',
  Editor = 'EDITOR',
  Viewer = 'VIEWER'
}

function checkPermission(role: UserRole) {
  if (role === UserRole.Admin) {
    // ...
  }
}

문제는 enum이 JavaScript로 컴파일될 때 실제 객체로 변환되면서 번들에 포함된다는 점이었다. Webpack 번들 분석 결과 사용하지 않는 enum 값도 모두 포함되는 것을 확인했다.

해결 방법

TypeScript 3.4부터 지원하는 as const assertion과 union type을 조합했다.

const UserRole = {
  Admin: 'ADMIN',
  Editor: 'EDITOR',
  Viewer: 'VIEWER'
} as const;

type UserRole = typeof UserRole[keyof typeof UserRole];

function checkPermission(role: UserRole) {
  if (role === 'ADMIN') {
    // ...
  }
}

이 방식은 런타임에 객체가 남지만, tree-shaking이 더 잘 작동했다. 더 나아가 리터럴 타입만 사용하는 방식도 고려했다.

type UserRole = 'ADMIN' | 'EDITOR' | 'VIEWER';

function checkPermission(role: UserRole) {
  // 타입 안정성은 동일하게 유지
}

결과

  • 번들 사이즈 약 2KB 감소
  • 타입 안정성은 동일하게 유지
  • 코드 자동완성도 정상 작동

enum이 필요한 경우(비트 플래그 등)가 아니라면 union type 사용을 기본으로 가져가기로 했다. 팀 코딩 컨벤션에도 반영했다.

TypeScript 3.6 enum 대신 union type 사용하기