개요
TypeScript에서 enum, const enum, as const는 값 집합을 선언하고 타입으로 활용하기 위한 서로 다른 도구임 각 특성의 차이와 트레이드오프를 이해하면 가독성과 안정성을 챙기면서 번들 크기와 도구 호환성 문제도 피할 수 있음
enum 개념과 동작
- 열거형 타입을 선언하는 문법
- 숫자 기반과 문자열 기반 모두 지원
- 컴파일 결과로 양방향 매핑을 담은 JS 객체 생성
- 키로 값 조회, 값으로 키 역조회 가능
예시
enum BooleanType {
False = 0,
True = 1,
}
컴파일 결과는 즉시실행함수 형태로 양방향 맵 객체 생성됨
var BooleanType = { 0: "False", 1: "True", False: 0, True: 1 };
주의점
- 멤버 이름에 숫자 이름 사용 불가
- 예) enum X { 1: ‘A’ } 에러 발생
멤버 종류
- Constant Member: 고정 상수값 보유
- Computed Member: 표현식 평가 결과를 값으로 가짐
값 타입별 종류
- Numeric enum
- String enum
- Heterogeneous enum (숫자와 문자열 혼합 사용, 특별한 이유 없으면 지양)
자동 할당
- 첫 멤버 미초기화 시 0부터 시작
- 이전 멤버가 숫자 상수면 다음 미초기화 멤버는 이전값 + 1 할당
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
장점
- 의도 표현력 높음, 양방향 매핑으로 상수 집합을 하나의 식별자로 추상화 가능
- 정의된 값만 사용하도록 강제 가능, 오입력 방지에 유리
단점
- 컴파일된 JS 코드가 객체와 초기화 로직을 포함하여 번들 크기 증가
- IIFE로 생성되는 사이드이펙트 때문에 일반적으로 트리 셰이킹에 취약
const enum
enum의 코드 비용과 간접 참조 비용을 줄이는 목적의 변형 문법 핵심 특징
- 멤버 접근 지점에 값이 인라인됨
- 컴파일 시 enum 구현체가 제거됨
- Computed Member 금지, 상수 표현식만 허용
예시
const enum Direction {
Up,
Down,
}
const xs = [Direction.Up, Direction.Down];
컴파일 결과에서 배열 리터럴에 숫자 값이 직접 인라인됨
- 멤버는 문자열 리터럴 접근으로만 사용 가능하지 않은 방식은 에러 발생
장점
- 실행 시 오브젝트 생성 비용 제거, 번들 크기 감소 가능
주의 및 함정
- 빌드 파이프라인에서 트랜스파일러가 const enum 인라인을 보장하지 않으면 깨짐
- 예) 일부 Babel, isolatedModules 환경, 타입 정보 손실되는 트랜스파일 경로 등
- 라이브러리 경계 넘어 사용하는 경우 소비자 빌드 설정에 따라 오류 유발 가능 이유로 프로덕션 코드베이스에서 const enum 사용을 제한하거나 금지하는 팀이 많음
as const
TypeScript 3.4의 const assertion 문법 목적
- 값의 타입 추론 범위를 리터럴 단위로 좁히기 위한 선언적 방법
- 객체, 배열, 중첩 구조를 포함해 값 전반을 상수로 취급하게 함
핵심 효과
- 문자열/숫자 값이 리터럴 타입으로 고정됨
- 객체 수준으로 사용 시 프로퍼티에 readonly 적용됨
간단 예시
const s = "NLP" as const; // 타입이 'NLP'로 고정
const obj = { a: 1, b: "x" } as const;
// obj.a: 1, obj.b: 'x', 모두 readonly
let으로 선언한 값에도 as const를 붙이면 리터럴 타입으로 고정됨
- 재할당 또는 변경 시 타입 오류 또는 readonly 위반으로 막힘
Discriminated Union과의 결합
조건 분기에서 안전하게 속성을 접근하려면 판별 가능한 유니온이 유용함
- 공통 판별 키 kind 등으로 각 분기를 좁혀 타입 안전한 접근 보장
- as const로 리터럴 값을 고정하면 판별 키가 확정되어 오류 제거에 도움
예시
type Shape = { kind: "circle"; radius: number } | { kind: "square"; x: number };
function area(s: Shape) {
if (s.kind === "circle") return Math.PI * s.radius * s.radius;
return s.x * s.x;
}
객체 리터럴 작성 시 kind 값을 as const로 고정하면 판별 정확도 향상
const enum 대체 패턴(as const 기반)
const enum 없이도 값 집합을 타입으로 뽑아 쓰는 패턴
const BooleanType = { False: 0, True: 1 } as const;
type BooleanTypeValue = (typeof BooleanType)[keyof typeof BooleanType]; // 0 | 1
특징
- 런타임 객체는 단방향 매핑만 가짐
- 타입은 값 집합의 유니온으로 안전하게 반영됨
- 번들에 불필요한 초기화 함수 없음, 트리 셰이킹 친화적
- enum처럼 값→키 역조회가 필요하면 별도 맵 구성 필요
선택 기준
이중 매핑 필요 여부가 1차 기준
- 값↔키 양방향 매핑과 역조회가 유용한 도메인이면 enum 고려
- 매핑 방향이 단방향이고 값 집합을 타입으로만 활용하면 as const + 유니온 추출 권장
빌드 환경과 도구 호환성도 중요
- const enum은 인라인이 보장되는 단일 tsc 파이프라인에서만 안정적
- 다양한 트랜스파일 단계나 라이브러리 경계를 넘는 경우 as const 대안이 안전
요약
- enum은 의도 표현과 역조회가 필요한 경우 적합하지만 번들 비용과 트리 셰이킹 한계 존재
- const enum은 이론상 가장 가벼우나 빌드 파이프라인 제약과 함정으로 실무에서 채택 신중 필요
- as const는 리터럴 타입 고정과 readonly 부여로 타입 안전성을 높이고, const enum의 주요 사용처를 무리 없이 대체함
마무리
양방향 매핑과 역조회가 반드시 필요하면 enum 선택 그 외 대부분의 상수 집합과 타입 추론 고정 요구에는 as const 기반 패턴 권장 const enum은 빌드 구성과 팀 규칙을 충분히 검토한 뒤 제한적으로 사용 권장
참고자료
- https://www.typescriptlang.org/docs/handbook/enums.html
- https://www.typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls
- https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types
- https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
- https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions
- https://www.kabuku.co.jp/developers/good-bye-typescript-enum