Develop Note by J.S.

[Typescript] Enum과 Union Type 본문

Language/Typescript

[Typescript] Enum과 Union Type

js-web 2024. 1. 30. 14:00
반응형

1. Enum

- Enum은 열거형 변수를 객체행태로 정의한 상수의 집합을 생성합니다. 임의의 숫자 또는 문자열을 상수화하여 관리하는 기능입니다. 

// 숫자 타입(열거형)
enum Fruit {
    apple, // 1
    banana, // 2
    carrot, // 3
}

// 문자열 타입
enum Fruit {
    apple = 'apple',
    banana = 'banana',
    carrot = 'carrot'
}

function setFruit(fruit :Fruit) {
	// ...
}
setFruit(Fruit.apple);

 

-  이렇게 편리해보이는 Enum도 3가지 문제로 인해 대부분의 커뮤니티에서는 Enum 대신 Union Type 사용을 권장하고 있습니다. 

 

1) 타입 오염 (Heterogeneous enums)

- 숫자 타입과 문자열 타입을 나누어 지는데 먼저 문자열 타입 사용 시의 문제점 입니다. 

enum Fruit {
    apple = 'apple',
    banana = 'banana',
    carrot = 'carrot'
}

function setFruit(fruit: Fruit) {}

setFruit('apple') 
// TS2345: Argument of type '"apple"' is not assignable to parameter of type 'Fruit'.

- 이렇게 상수값으로 지정한 'apple'은 직접 인자로 사용 할 수 없지만, Union Type의 경우는 가능합니다.  만약 Fruit에 숫자형 타입을 추가하게 될 경우에는 어떻게 될까요?

enum Fruit {
    apple = 'apple',
    banana = 'banana',
    carrot = 'carrot',
    orange = 1
}

console.log(Object.keys(Fruit)) //  ["1", "apple", "banana", "carrot", "orange"] 
// Object.Keys 반환 값에 1이 포함되어 있는 것을 볼 수 있습니다.

function setFruit(fruit :Fruit) {
	// ...
}

// 이렇게 될 경우 문자타입과는 다르게 숫자타입으로 지정한 orange의 상수 1은 허용 될 수 있습니다. 
setFruit(1);

2) 직렬화 문제

- 숫자 타입의 Enum 사용 시 직렬화 문제가 발생됩니다. 

enum Fruit {
    apple, // 1
    banana, // 2
    carrot,
    orange
}

console.log(Fruit.banana); // 2

- 기존에 banana의 값은 순서상 2의 숫자형 상수값으로 설정되어 사용되고 있지만 만약 apple과 banana 사이에 다른 속성을 추가할 경우 그 아래 속성들은 모두 설정된 상수값이 변동되는 문제가 발생되게 됩니다. 

enum Fruit {
    apple, // 1
    melon, // 2
    banana, // 3
    carrot,
    orange
}

console.log(Fruit.banana); // 3

 

3) Tree-Shking 관점에서의 Enum

* TreeShaking

Tree-Shaking 이란 나무를 흔들면 죽은 나뭇잎이 떨어진다는 뜻으로, 코드의 빌드 시점에 사용되지 않는 코드를 자동으로 번들에 포함하지 않게 빌드하는 방식을 말합니다. TreeShking을 통해 번들 사이즈를 줄여 렌더링 시간 단축 등의 성능 개선 목적으로 사용됩니다.

 

- Tree-Shaking과 Enum의 관계를 설명하자면, Typescript에서 Enum사용 시 Tree-Shaking 되지 않는 문제가 있습니다. Typescript 컴파일러는 Enum 사용 시 아래와 같은 즉시 실행 함수를 포함한 코드를 생성합니다. 그런데 Rollup과 같은 번들러의 경우 즉시실행 함수를 '미사용 코드' 로 판단하기 어려워 Tree-Shaking 되지 않습니다. 

// enum
export enum TreeShakingTest1 {
    test1,
    test2,
    test3,
}

// 빌드 시
TreeShakingTest1:Gp
...
Gp= (e=> (
    e[e.test1=0]="test1",
    e[e.test2=1]="test2",
    e[e.test3=2]="test3",
    e))(Gp||{})

 

 

- 여기까지  앞서 설명드린 3가지 이유 때문에 Enum 대신 Union Type사용을 권장하고 있습니다. 

 

2. typeof, keyof 연산자

- Union Type 형식의 상수관리는 typeof, keyof 연산자를 사용합니다.

1) typeof

 - 타입스크립트에서 특정 변수에 typeof 연산자를 사용하면 특정 변수 자체의 타입을 정의할 수 있습니다.

let str = 'apple'

let fruit: typeof str = 'banana' // typeof str은 string 타입

let obj = {
   apple: 'apple',
   banana: 'banana',
   orange: 'orange'
}

type Fruit = typeof obj;

// typeof obj는  아래와 같다
type Fruit {
    apple: string,
    banana: string,
    orange: string 
}

// type Fruit를 타입으로하는 신규객체를 생성할 경우 obj 객체와 동일한 key 명과 타입으로 생성해야한다
let newObj: Fruit = {
   apple: 'apple2',
   banana: 'banana2',
   orange: 'orange2'
}

 

2) keyof

- keyof는 객체 타입의 각 key를 유니온 타입으로 구성해주는 연산자 입니다. 

type fruit = {
    apple: 'apple',
    banana: 'banana',
    orange: 'orange
}

type unionFruit = keyof fruit; // 'apple' | 'banana' | 'orange' 와 동일

* Union Type : 자바스크립트의 OR 연산자(||)와 같이 A이거나 B이다 라는 의미의 타입입니다. 

 

3. Union Type

- typeof, keyof 연산자로 유니온 타입 활용을 이용하면 enum의 문제를 해결 할 수 있습니다. 

const TreeShakingTest3 = {
    test1: 1,
    test2: 2,
    test3: 3,
} as const;

type TypeTreeShakingTest3 = typeof TreeShakingTest3[keyof typeof TreeShakingTest3];
// keyof typeof TreeShakingTest3의 결과는 객체의 Key 이루어진 Union Type을 리턴 한뒤
// typeof TreeShakingTest3[Key Union Type] 으로 value로 이루어진 Union Type으로 Typing 한다.

const test1: TypeTreeShakingTest3 = TreeShakingTest3.test1;

 

- as const의 경우 const로 선언된 TreeShakingTest3 객체의 내부 속성은 변동 될 수 있지만 as const를 사용하면 리터럴 타입의 추론범위를 줄이면서 재할당을 막을 수 있습니다.

test1: number로 타입설정

 

 

test1: 1로 타입설정

- Union Type을 사용한 경우 빌드 시 미사용된 코드라면 Tree-Shaking에 의해 번들에 포함되지 않습니다. 

 

 

 

참고사이트

https://joshua1988.github.io/ts/guide/operator.html

https://velog.io/@ansunny1170/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-enum%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%95%88%EB%90%A0%EA%B9%8C

https://blog.toycrane.xyz/typescript%EC%97%90%EC%84%9C-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%83%81%EC%88%98-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-e926db079f9

https://velog.io/@ddaisylee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-keyof-typeof

반응형