[240131] Typescript enum 사용을 지양하는 이유들

Tree-shaking 관점

Tree-shaking 이란?

  • 사용하지 않는 코드를 삭제하는 기능
  • 나무를 흔들면 죽은 잎사귀들이 떨어지는 모습에 착안
  • export 했지만 아무 곳에서 import 하지 않은 모듈이나 사용하지 않는 코드를 삭제해서 번들 크기를 줄여 페이지가 표시되는 시간을 단축할 수 있다.

TS 에서 enum 을 사용하면 Tree-shaking 이 되지 않는다.

  • enum 은 ts 가 자체적으로 구현했기때문

image-1

  • enum 을 트랜스파일하면 위와 같은 JavaScript 코드가 된다.
  • JS 에 존재하지 않는 것을 구현하기 위해 TS 컴파일러는 IIFE(즉시 실행 함수)를 포함한 코드를 생성한다. 근데 Rollup 과 같은 번들러는 IIFE를 사용하지 않는 코드라고 판단할 수 없어서 Tree-shaking 이 되지 않는다.
  • 실제로 SNS 를 사용하지 않더라고 최종 번들에 포함

enum 대신 어떤 것을 사용하면 좋을까?

Union Types

image-2

  • IIFE 가 생성되지 않으므로 Tree-shaking 가능
  • 타입 이점도 그대로 누릴 수 있음.

const enum

image-3

  • const enum 은 Babel 로 전송할 수 없다.
/// a.ts
export const enum A {
  B
}
export const enum A {
  C = 2
}

/// b.ts
import { A } from './a'

console.log(A.B, A.C)
/// a.js
export var A;
(function (A) {
  A[(A["B"] = 0)] = "B";
})(A || (A = {}));
(function (A) {
  A[(A["C"] = 2)] = "C";
})(A || (A = {}));
//# sourceMappingURL=a.js.map

/// b.js
console.log(A.B, A.C);
//# sourceMappingURL=b.js.map
  • -isolatedModules 컴파일 옵션을 활성화하면 Ambient Context (d.ts, declare 구문) 에서 선언된 const enum 에 다른 모듈에서 액세스하면 컴파일 오류가 발생
    • const enum 의 출력 결과가 enum 과 같이 됨, 다른 모듈에 존재하는 const enum 정의 원래 정보를 참조할 수 없는 상황에선 const enum 의 원래 동작인 인라인화 할 수 없다.

tree-shaking 관점에서 보았을때

  • Union Types > const enum > enum

위와 같은 순서를 추천한다고 한다.


Union 방식 더 알아보기

Handbook - Enums

  • typescript 공식문서에서도 as const 를 사용해서 enum 을 대신하는 방법을 소개하고 있음
  • 객체 또는 배열을 as const 로 선언한 뒤, union 타입을 추출해서 사용하는 방식

Union 추출 코드 제네릭으로 추상화

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

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

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

as const

  • as const 를 사용하면 원시 타입이든 참조 타입이든 값의 재할당을 막아버림
  • 또한 리터럴 타입의 추론 범위가 리터럴 값 자체로 한정 됨
const greeting1 = "Hello, World!"; // "Hello, World!"로 추론
let greeting2 = "Hello, World!"; // string으로 추론
let greeting2 = "Hello, World!" as const;

let 을 선언된 변수의 타입을 리터럴로 한정하고 싶다면 as const 로 type assertion 을 해주면 된다.

const language = {
  korean: "ko",						// string으로 추론
  english: "en",					// string으로 추론
};
language.korean = "kkk";	// kkk로 korean프로퍼티가 변경
language.english = "eee"; // eee로 english프로퍼티가 변경

const language = {
  korean: "ko",						// "ko"로 추론, readonly 프로퍼티로 변경
  english: "en",					// "en"로 추론, readonly 프로퍼티로 변경
} as const;

// Error: Cannot assign to 'korean' because it is a read-only property
language.korean = "kkk";
// Error: Cannot assign to 'english' because it is a read-only property.
language.english = "eee";
  • 객체는 참조 타입이라 const 로 선언하더라도, 내부 프로퍼티 값은 바뀔 수 있는데 as const 를 사용하면 재할당을 막을 수 있다.

const enum

  • 최종 결과물에 객체 리터럴 존재하지 않음
  • reverse mapping 이 필요한 경우가 아니라면 const enum 을 사용하자

참고자료

Enum vs as const

さようなら、TypeScript enum - 株式会社カブク

TypeScript enum을 사용하지 않는 게 좋은 이유를 Tree-shaking 관점에서 소개합니다.

enum type 대신 union type으로 변경하기

Categories:

Updated:

Leave a comment