[TypeScript] Enums
정의
Enum 은 여러 관련 상수를 하나의 이름 아래 그룹화하는 TypeScript 기능입니다. Numeric enum, string enum, const enum, heterogeneous enum 등 여러 변종이 있으며, 최근에는 enum 을 지양하고 union literal 이나 as const 객체를 쓰는 편이 관용 이 되고 있습니다.
Numeric Enum
기본 형태. 값이 정수로 자동 할당.
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
const d = Direction.Up; // 0
명시적 값:
enum Status {
OK = 200,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
ServerError = 500,
}
Reverse mapping
Numeric enum 은 값 -> 이름 역방향도 자동 생성:
console.log(Status[200]); // "OK"
console.log(Status[404]); // "NotFound"
컴파일된 JS:
var Status;
(function (Status) {
Status[Status["OK"] = 200] = "OK";
Status[Status["BadRequest"] = 400] = "BadRequest";
})(Status || (Status = {}));
String Enum
값이 문자열. Reverse mapping 없음.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
console.log(Direction.Up); // "UP"
console.log(Direction["UP"]); // undefined (reverse mapping 없음)
디버깅 시 값이 명시적이라 numeric enum 보다 유리.
Heterogeneous Enum
숫자/문자열 혼합. 실전에서 지양.
enum Mixed {
A = 1,
B = "yes",
}
Const Enum
const enum 은 컴파일 시 inline 됩니다. 런타임 객체가 없음.
const enum Direction {
Up,
Down,
Left,
Right,
}
const d = Direction.Up;
컴파일 결과:
const d = 0 /* Direction.Up */;
Pros:
- 런타임 오버헤드 없음
- 트리 셰이킹 우수
Cons:
isolatedModules: true하에서 사용 불가 (esbuild/swc/Babel 이 파일 단위 트랜스파일이라 다른 파일의 const enum 참조 불가)- 라이브러리 배포 시 문제 (사용자가 참조 불가)
결론: 모던 프로젝트에서는 사실상 사용 불가. as const 객체가 대안.
Computed Members
enum FileAccess {
None,
Read = 1 << 1, // 2
Write = 1 << 2, // 4
ReadWrite = Read | Write, // 6
Length = "abc".length, // 3 (compile-time computed)
}
Bitwise flag 에 유용하지만 코드 리뷰가 힘들어짐.
Enum 의 문제점
1. isolatedModules 하에서 const enum 불가
esbuild, swc, Babel 이 fail. 모던 툴체인에서 큰 제약.
2. Numeric enum 은 타입 안전성이 부족
enum Direction { Up, Down }
function move(d: Direction): void { ... }
move(Direction.Up); // OK
move(100); // 오류 아님! (numeric 이면 any number 허용될 수 있음)
TypeScript 5.0+ 부터는 조금 더 엄격해졌지만 여전히 함정 존재.
3. Reverse mapping 은 종종 유출
Numeric enum 의 reverse mapping 은 원치 않을 때가 많음.
const values = Object.values(Status);
// = ["OK", "BadRequest", ..., 200, 400, ...] (문자열 + 숫자)
4. 트리 셰이킹 실패
일반 enum (non-const) 은 런타임 객체를 만들어 트리 셰이킹 안 됨. 큰 enum 은 번들 크기 증가.
대안 1: Union Literal
가장 간단하고 관용적:
type Direction = "up" | "down" | "left" | "right";
function move(d: Direction): void { ... }
move("up"); // OK
move("northwest"); // 오류
// Exhaustive check
function opposite(d: Direction): Direction {
switch (d) {
case "up": return "down";
case "down": return "up";
case "left": return "right";
case "right": return "left";
}
}
Pros:
- 런타임 오버헤드 없음 (그냥 문자열)
- Structural typing 과 잘 맞음
isolatedModulesOK- 트리 셰이킹 완벽
Cons:
- 값 목록을 조회하기 힘듦 (
Object.values(enum)대안이 없음)
대안 2: as const Object
값 목록도 필요하면:
const Direction = {
Up: "up",
Down: "down",
Left: "left",
Right: "right",
} as const;
type Direction = typeof Direction[keyof typeof Direction];
// = "up" | "down" | "left" | "right"
function move(d: Direction): void { ... }
move(Direction.Up); // OK
move("up"); // OK
// 값 열거
Object.values(Direction); // ["up", "down", "left", "right"]
Pros:
- 런타임 객체 존재 (
Object.keys,Object.values) - 타입 안전 (union 로 좁혀짐)
as const로 정확한 리터럴 타입isolatedModulesOK- 트리 셰이킹 OK (import 되지 않으면 제거)
Cons:
- 코드가 살짝 장황
이 방식이 2020년대 관용의 정답.
Union + Object 실전 예시
export const HTTP_STATUS = {
OK: 200,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
NOT_FOUND: 404,
SERVER_ERROR: 500,
} as const;
export type HttpStatus = typeof HTTP_STATUS[keyof typeof HTTP_STATUS];
// = 200 | 400 | 401 | 404 | 500
function respond(status: HttpStatus): void { ... }
respond(HTTP_STATUS.OK); // OK
respond(200); // OK (literal 이라)
respond(300); // 오류
Union + Function 헬퍼
export type Role = "admin" | "editor" | "viewer";
export const ROLES: Role[] = ["admin", "editor", "viewer"];
export function isRole(x: unknown): x is Role {
return typeof x === "string" && ROLES.includes(x as Role);
}
Enum 유지가 여전히 나은 케이스
- legacy 프로젝트 이미 enum 다수 사용 중
- NestJS / Angular 데코레이터와 결합
- 양방향 매핑 (numeric <-> string 이름) 이 자주 필요
- Bitwise flag (실전에서는 여전히 유용)
Enum 리팩토링
// Before
enum Direction { Up, Down, Left, Right }
// After (마이그레이션)
export const Direction = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const;
export type Direction = typeof Direction[keyof typeof Direction];
이 이전은 usage 코드 변경 없이 진행 (Direction.Up 그대로 작동).
Enum 을 iterable 로 만들기
enum Direction { Up, Down, Left, Right }
const directions = Object.values(Direction).filter(
v => typeof v === "number"
) as Direction[];
for (const d of directions) {
console.log(d);
}
이 필터가 필요한 이유가 numeric enum 의 reverse mapping 때문. 그래서 as const 객체가 더 깔끔.
함정
WARNING
isolatedModules 하에서 const enum 오류. esbuild, Vite, Next.js 대부분에서 사용 불가.
CAUTION
Numeric enum 은 임의 숫자 허용 위험. TS 5.0+ 개선되었지만 완전히 안전한 것은 아님.
WARNING
Enum 을 export 하는 라이브러리. 사용자의 프로젝트에서 다르게 취급될 수 있음 (const enum 의 문제). String union 이 안전한 API.
IMPORTANT
새 코드는 as const object 또는 union literal. Enum 은 legacy / 특수 케이스만.
CAUTION
Reverse mapping 유출. Object.values(numericEnum) 는 값 + 이름 모두. 필터 필요.
관련 위키
- TypeScript - 상위 개요
- Union / Intersection - Literal union
- Primitive Types - Literal type
- Type Aliases - as const
- Modules - isolatedModules
- Strict Mode
- Type Narrowing - Exhaustive check
이 글의 용어 (7개)
- [TypeScript] Modules (import/export, moduleResolution)typescript
- 정의 TypeScript 모듈 시스템은 ES Modules (ESM) 를 기본 언어 모델로 삼되, CommonJS (CJS) 를 상호운용성 (interop) 계층으로 지원합니다.…
- [TypeScript] Primitive Typestypescript
- 정의 Primitive Types 는 TypeScript 의 가장 기본 타입 계열입니다. JavaScript 의 7가지 primitive 값 + 특수 타입 (void, never…
- [TypeScript] Strict Mode & tsconfigtypescript
- 정의 Strict Mode 는 여러 엄격한 타입 검사 플래그를 한 번에 켜는 옵션 ( ) 입니다. 강력한 타입 안전성을 제공하지만, 기존 코드베이스에는 대량의 오류를 발생시킬 수…
- [TypeScript] Type Aliasestypescript
- 정의 Type Alias 는 타입에 이름을 붙이는 TypeScript 문법 ( ). Interface 는 객체 shape 만 표현하지만, type alias 는 모든 타입 표현식…
- [TypeScript] Type Narrowing (Type Guards)typescript
- 정의 Type Narrowing 은 TypeScript 컴파일러가 제어 흐름 분석 (control flow analysis) 을 통해 특정 위치에서 값의 타입을 더 좁게 추론하는…
- [TypeScript] Union & Intersection Typestypescript
- 정의 - Union Type ( ): A 이거나 B (또는 둘 다) 인 값 - Intersection Type ( ): A 이면서 B 인 값 두 조합은 TypeScript 의 타…
- TypeScripttypescript
- 정의 TypeScript 는 Microsoft 가 2012년 발표한 JavaScript 의 상위 집합 프로그래밍 언어입니다. 정적 타입 시스템, 인터페이스, 제네릭, enum, …
💬 댓글