Enum(Enumeration, 열거형)

Gom·2021년 7월 17일
0

TypeScript

목록 보기
2/5

Enum(Enumeration, 열거형)

  • 의미 : 열거형 변수, 이름있는 상수들의 집합을 정의할 수 있다.
  • 필요한 상황 :
    유한하면서도 여러가지의 상태를 가질 수 있는 변수를 정의하고 싶은 경우
    enum을 활용하면 의도를 문서화하거나 구분되는 사례 집합을 쉽게 만들 수 있다.
  • 표현 : enum 키워드를 이용한다.
enum Color {
  RED,
  GREEN, 
  BLUE
}

function setColor(color: Color) {
// ...
}

setColor(Color.RED);

1. 선언과 값 초기화

1-1. 숫자 열거형 (Numeric enums)

  • enum으로 만들어진 변수에는 내부적으로 값이 할당된다.
  • 별도의 명시가 없다면 값은 0부터 시작해서 1씩 증가하는 형태로 할당된다. 자동-증가하는 기능은 멤버 값 자체에는 신경 쓰지 않지만, 각 값이 같은 열거형의 다른 값과 구별돼야 하는 경우에 유용하다.
enum Direction {
    Up, // 0
    Down, // 1
    Left,
    Right,
}
  • 원하는 값, 간단한 연산자를 사용한 표현식으로 초기화도 가능하다.
enum Color {
  RED = 10,
  GREEN = 20,
  BLUE = RED + GREEN // 런타임이 아닌 컴파일 타임에 30이라는 값이 할당된다.
} 
  • 문자열로 enum 값을 초기화하는 경우 reverse mapping을 지원하지 않는다.
enum Color {
  RED = "red",
  GREEN = "green",
}

console.log(Color[Color.RED]); //undefined

특징 및 유의사항

Computed and constant members

  • 초기화 용도의 표현식은 런타임이 아닌 컴파일 타임에 평가된다는 특징이 있다. 물론 enum 내부에서 알 수 없는 외부 변수를 사용하는 경우에는 표현식이 유지되었다가 추후 할당된다.

  • 숫자 열거형은 계산된 멤버와 상수 멤버를 혼용해 사용할 수 있다.

    • 계산된 멤버와 상수 멤버
      연산자가 포함된 모든 표현식이 계산된 멤버는 아니다.
      아래의 경우 상수 열거형 표현식이라고 하며 그 외 다른 경우를 계산된 멤버로 간주한다.

      1. 리터럴 열거형 표현식 (문자 리터럴, 숫자 리터럴)
      2. 이전에 정의된 다른 상수 열거형 멤버에 대한 참조 (다른 열거형에서 시작될 수 있음)
      3. 괄호로 묶인 상수 열거형 표현식
      4. 상수 열거형 표현식에 단항 연산자 +, -, ~를 사용한 경우
      5. 상수 열거형 표현식을 이중 연산자 +, -, *, /, %, >>, <<, >>>, & , | , ^ 의 피연산자로 사용한 경우
      • 주의 : 상수 열거형 표현식 값이 NaN이거나 Infinity이면 컴파일 시점에서 오류 발생
    • 이 때 허용 가능한 순서에 유의해야 한다.
      초기화되지 않은 열거형이 먼저 나오거나, 숫자 상수 혹은 다른 상수 열거형 멤버와 함께 초기화된 숫자 열거형 이후에 와야한다.

      case 1 : 계산된 멤버가 초기화되지 않은 열거형 앞에 올 수 없다.
      case 2 : 초기화 값이 없거나 숫자 상수로 초기화된 열거형 멤버 뒤에 오는 경우 앞 상수 값에 1씩 증가한 값을 상수로 갖는다.

//case 1
enum E {
    A = getSomeValue(),
    B, // 오류! 앞에 나온 A가 계산된 멤버이므로 초기화가 필요합니다.
}

//case 2
enum E1 {
  X, // 0
  Y, // 1
  Z
}

enum E2 {
  A = 1, 
  B, // 2
  C
}

1-2. 문자열 열거형 (String enums)

  • 문자열 열거형에서 각 멤버들은 문자열 리터럴 또는 다른 문자열 열거형의 멤버로 상수 초기화 해야한다.
  • 숫자형처럼 자동-증가하는 기능과 reverse mapping 기능은 없지만 유의미하고 읽기 좋은 값을 할당할 수 있다.

1-3. 이종 열거형 (Heterogeneous enums)

  • 기술적으로 숫자와 문자를 섞어 사용 가능하지만 굳이 그렇게 할 이유는 없다.
  • JS 런타임에서 값을 활용하는 장점을 취하려는 것이 아니라면 이렇게 사용하지 않는 것을 권장하고 있다.
enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}

2. 트랜스파일링

  • enum은 TypeScript에서 자체적으로 제공하는 기능으로 JavaScript에서는 사용할 수 없다.
  • TypeScript로 작성한 enum은 아래와 같이 트랜스파일된다.
// ts로 작성된 enum
enum Color {
  RED,
  GREEN, 
  BLUE
}

// 트랜스 파일 후
var Color;
(function (Color) {
  Color[Color["RED"] = 0] = "RED";
  Color[Color["GREEN"] = 1] = "GREEN";
  Color[Color["BLUE"] = 2] = "BLUE";
})(Color || (Color = {}));

// 위 즉시실행 함수는 결과적으로 아래와 같은 객체가 된다.
var Color = {
  RED: 0,
  GREEN: 1,
  BLUE: 2,
  0: "RED",
  1: "GREEN",
  2: "BLUE"
};
  • reverse mapping을 지원한다.
    (키로 값을 얻을 수 있을 뿐 아니라 값으로도 키를 얻을 수 있는 방식을 말한다. 트랜스파일 결과로 생성된 객체의 형태를 통해 이해할 수 있다.)

3. const enum

  • enum 키워드 앞에 const와 함께 사용할 수 있다. 이 경우 트랜스파일 결과물을 가지지 않는다. (= 트랜스파일 결과 생성되던 객체가 없다.)
  • 차이 1 :
    기존에 정의했던 값을 사용하는 것을 문제가 없으나 객체가 생성되지 않으므로 런타임에서 Color에 접근할 수 없게 된다. 런타임에서 enum 변수의 값을 변경할 수 있었던 문제를 방지할 수 있다.
  • 차이 2 :
    reverse mapping을 지원하지 않는다. reverse mapping이 런타임에 존재하는 객체를 이용하는 것이기 때문이다.
  • 차이 3 :
    컴파일 타임에 평가할 수 없는 표현식으로 값을 할당할 수 없다. 본래 enum은 간단한 표현식으로 초기화가 가능하고 enum 내부에서 확인이 가능한 경우 컴파일 타임에서 값을 할당하고 외부 변수를 참조해야 하는 경우 컴파일 타임에 변환되지 않고 런타임에 평가되었다. 이렇게 되면 항상 같은 값임을 보장할 수 없으므로 const enum은 이를 허용하지 않는다. enum 키워드 바깥에서 정의한 변수를 표현식에 포함하면 컴파일 에러를 낸다.
    ERROR: [ts] In 'const' enum declarations member initializer must be constant expression.

추가 자료
enum을 사용하지 않는 게 좋은 이유 - Tree-shaking관점

참고 자료
https://hyunseob.github.io/2017/07/18/typescript-enums/

profile
안 되는 이유보다 가능한 방법을 찾을래요

0개의 댓글