ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TypeScript | Enum 열거형 타입
    JavaScript/TypeScript 2020. 5. 6. 15:13

    Enum 열거형 타입

    열거형 타입은 enum 키워드를 사용해서 정의
    열거형 타입의 각 원소는 값 또는 타입으로 사용 가능

    // 열거형 타입으로 과일 정의
    enum Fruit {
        Apple,
        Banana,
        Orange
    }
    
    // 열거형 타입의 원소인 Apple을 값으로 사용
    const v1: Fruit = Fruit.Apple;
    
    // 열거형 타입의 원소인 Apple, Banana를 타입으로 사용
    const v2: Fruit.Apple | Fruit.Banana = Fruit.Banana;

    enum 원소의 값

    enum Fruit {
        Apple,
        Banana = 5,
        Orange
    }
    
    console.log(Fruit.Apple, Fruit.Banana, Fruit.Orange);
    // 0 5 6
    • 열거형 타입의 첫 번째 원소에 값을 할당하지 않으면 자동으로 0 할당
    • 열거형 타입의 각 원소에 숫자 또는 문자열 할당 가능
      명시적으로 값을 입력하지 않으면 이전 원소에서 1만큼 증가한 값 할당

    객체로 존재하는 열거형 타입

    // 타입스크립트는 위의 코드를 아래와 같이 컴파일
    var Fruit;
    (function(Fruit){
        Fruit[(Fruit['Apple'] = 0)] = 'Apple';
        Fruit[(Fruit['Banana'] = 5)] = 'Banana';
        Fruit[(Fruit['Orange'] = 6)] = 'Orange';
    })(Fruit || (Fruit = {}));
    console.log(Fruit.Apple, Fruit.Banana, Fruit.Orange);
    // 0, 5, 6
    • 열거형 타입은 객체로 존재
    • 열거형 타입의 각 원소는 이름과 값이 양방향으로 매핑(mapping)
    • 열거형 타입은 객체로 존재하기 때문에 해당 객체를 런타임에 사용 가능

    열거형 타입 객체 사용하기

    enum Fruit {
        Apple,
        Banana = 5,
        Orange
    }
    console.log(Fruit.Banana); // 5
    console.log(Fruit['Banana']); // 5
    console.log(Fruit[5]); // Banana
    • 열거형 타입은 객체이기 때문에 일반적인 객체처럼 다룰 수 있음
    • 각 원소의 이름과 값이 양방향으로 매핑되어 있기 때문에 값을 이용해서 이름을 가져올 수 있음
      단, 서로 다른 원소에 같은 수를 할당하는 경우 마지막 원소의 이름을 반환

    열거형 타입의 값으로 문자열 할당

    enum Language {
        Korean = 'ko',
        English = 'en',
        Japanese = 'jp'
    }
    
    console.log(Language.Korean); // ko
    console.log(Language['ko']); // 에러 발생
    
    // 컴파일 결과
    "use strict";
    var Language;
    (function (Language) {
        Language["Korean"] = "ko";
        Language["English"] = "en";
        Language["Japanese"] = "jp";
    })(Language || (Language = {}));
    • 열거형 타입의 원소에 문자열을 할당하는 경우 단방향으로 매핑됨
    • 이는 서로 다른 원소의 이름 또는 값이 같을 경우 충돌이 발생하기 때문
      → 컴파일 결과를 보면 위에서 봤던 컴파일 결과와 매핑 형식이 다름을 알 수 있음

    열거형 타입을 위한 유틸리티 함수

    열거형 타입을 자주 사용한다면 몇 가지 유틸리티 함수를 만들어서 사용하는 것이 좋음

    열거형 타입의 원소 개수를 알려 주는 함수

    function getEnumLength(enumObject: any) {
        const keys = Object.keys(enumObject);
        return keys.reduce(
            // enum 원소의 값이 숫자이면 2씩 증가하므로 문자열만 계산
            (acc, key) => (typeof enumObject[key] === 'string' ? acc + 1 : acc), 0
        )
    }
    • 원소의 값이 숫자인 경우에는 양방향으로 매핑되기 때문에 주의해야 함
    • 객체의 속성값이 문자열인 경우만 계산하면 열거형 타입에서 원소의 개수를 구할 수 있음

    열거형 타입에 존재하는 값인지 검사하는 함수

    function isValidEnumValue(enumObject: any, value: number | string) {
        if(typeof value === 'number'){
            return !!enumObject[value];
        } else {
            return (
            Object.keys(enumObject)
            .filter(key => isNaN(Number(key)))
            .find(key => enumObject[key] === value) != null
                )
        }
    }
    • 찾으려는 값이 숫자이면 양방향 매핑을 이용해서 검사
      !! → 값이 있는 경우 true를 반환하기 위함
    • 찾으려는 값이 문자열이면 양방향 매핑에 의해 생성된 키를 제거하고 해당 값이 존재하는지 검사
    • find 메서드에서 !=(엄격하지 않은 비교)를 사용하는 이유는 undefined를 가려내기 위함
      undefined !== null은 true를 반환

    간단한 사용 예제

    enum Fruit {
        Apple,
        Banana,
        Orange
    }
    
    enum Language {
        Korean = 'ko',
        English = 'en',
        Japanese = 'jp'
    }
    
    console.log(getEnumLength(Fruit), getEnumLength(Language)); // 3 3
    console.log('1 in Fruit', isValidEnumValue(Fruit, 1)); // true
    console.log('5 in Fruit', isValidEnumValue(Fruit, 5)); // false
    console.log('ko in Language', isValidEnumValue(Language, 'ko')); // true
    console.log('Korean in Language', isValidEnumValue(Language, 'Korean')); // false

    실용적 활용 예제

    isValidEnum 함수는 서버로부터 받은 데이터를 검증할 때 사용 가능

    getEnumLength 함수는 다음과 같이 실수 방지 용도로 사용 가능

    enum Fruit {
        Apple,
        Banana,
        Orange
    }
    
    // 과일의 가격 정보를 가진 객체
    const FRUIT_PRICE = {
        [Fruit.Apple]: 1000,
        [Fruit.Banana]: 1500,
        [Fruit.Orange]: 2000
    }
    
    // 이하 코드는 jest와 같은 테스팅 프레임 워크 필요
    test('FRUIT_PRICE에 정의되지 않은 Fruit가 있는지 체크' () => {
        expect(getEnumLength(Fruit)).toBe(Object.keys(FRUIT_PRICE).length);
    });
    • 과일 가격 정보 객체는 Fruit의 모든 원소를 갖고 있어야 함
    • 따라서 Fruit에 새로운 과일이 추가되면 FRUIT_PRICE에도 반드시 해당 과일의 가격을 추가해야 함
    • getEnumLength 함수를 이용해서 테스트 코드를 작성하면 Fruit와 FRUIT_PRICE의 크기를 항상 같게 유지할 수 있음

    상수 열거형 타입

    열거형 타입은 컴파일 후에도 남아 있기 때문에 번들 파일의 크기가 불필요하게 커질 수 있음
    상수(const) 열거형 타입을 사용하면 컴파일 결과에 열거형 타입의 객체를 남겨 놓지 않을 수 있음

    const enum Fruit {
        Apple,
        Banana,
        Orange,
    }
    const fruit: Fruit = Fruit.Apple;
    
    const enum Language {
        Korean = 'ko',
        English = 'en',
        Japanese = 'jp',
    }
    const lang: Language = Language.Korean;
    
    console.log(getEnumLength(Fruit)); // 컴파일 에러 발생
    
    // 컴파일 결과
    "use strict";
    const fruit = 0 /* Apple */;
    const lang = "ko" /* Korean */;
    • 컴파일 결과에 열거형 타입의 객체를 생성하는 코드가 보이지 않음
    • 열거형 타입이 사용된 코드는 원소의 값으로 대체되므로 코드가 간소화됨
    • 하지만 상수 열거형 타입을 사용하면 열거형 타입 객체 사용 불가

    참고도서: [실전 리액트 프로그래밍 / 저자_ 이재승 / 출판사_ 프로그래밍 인사이트]

    'JavaScript > TypeScript' 카테고리의 다른 글

    TypeScript | 타입 호환성  (0) 2020.05.07
    TypeScript | Interface 인터페이스  (0) 2020.05.07
    TypeScript | 함수형 타입  (0) 2020.05.06
    TypeScript | 개요 & 변수 타입  (0) 2020.05.06

    댓글

Designed by Tistory.