Typescript에는 enum 타입이 있습니다.
enum 타입은 열거형 타입으로 의미가 명확한 코드와 코드의 가독성을 제공하기 위해 존재합니다.
enum에는 숫자형 타입과 문자형 타입이 존재합니다.
숫자형 타입
enum DirectionNumber {
UP,
DONW,
RIGHT,
LEFT,
}
enum DirectionNumberWithNumber {
UP = 0,
DONW = 1,
RIGHT = 2,
LEFT = 3,
}
숫자형 타입에서는 value의 할당을 생략할 수 도 있습니다.
할당을 생략하게 되면 값은 0 부터 차례대로 할당됩니다.
(ex. UP = 0, DOWN = 1, RIGHT = 2, LEFT = 3)
문자형 타입
enum DirectionString {
UP = "up",
DONW = "down",
RIGHT = "right",
LEFT = "left",
}
문자형 타입에서는 값의 할당을 해주어야 합니다.
만약에 빼먹으면 숫자형 타입으로 인식이 됩니다.
빼먹었을때 경우에 따라 error를 발생하는데 아래에서 살펴보겠습니다.
잠깐 딴길로 새면
enum에서는 키 값들이 상수로 취급됩니다.
따라서 값은 enum내에서는 같은 키값을 사용할 수가 없습니다.
enum test {
a: string,
a: string, // error
}
또한 enum은 선언적 할당을 이용해 확장할 수 있습니다.
enum DirectionNumber {
UP,
DONW,
RIGHT,
LEFT,
}
enum DirectionNumber {
CENTER = 5,
}
console.log(DirectionNumber)
/*
{
'0': 'UP',
'1': 'DONW',
'2': 'RIGHT',
'3': 'LEFT',
'5': 'CENTER',
UP: 0,
DONW: 1,
RIGHT: 2,
LEFT: 3,
CENTER: 5
}
*/
하지만 선언이후에 값을 변경할수는 없습니다.
아래와 같이 기존의 키 값에 존재하지 않는 키값을 추가해줄순 있지만
원래 enum의 역할은 할수가 없습니다.
모호한 타입
enum DirectionString {
MIDDLE = 0,
UP = "up",
DONW = "down",
RIGHT = "right",
CENTER = 1,
LEFT = "left",
}
숫자형과 문자형을 섞으면 위와 같은 복잡한 enum이 생성되게 됩니다.
공식문서에서는 위의 타입을 추천하지 않고 있습니다.
만약 위의 모호한 타입에서 숫자형 타입의 값을 할당하지 않으면 어떻게 될까요??
enum DirectionString {
MIDDLE, // 0으로 할당
UP = "up",
DONW = "down",
RIGHT = "right",
CENTER, // error
LEFT = "left",
CENTER, // error
}
비할당된 타입이 첫번째 위치에 올때는 0부터 인식이 됩니다.
하지만 중간이나 마지막에 위치하게 되면 원하는대로 동작하지 않습니다.
숫자형 타입과 문자형타입의 차이는 타입의 종류 뿐만 아니라 transpile(ts -> js) 후에도 나타납니다.
숫자형, 문자형 타입을 두개를 선언하고 결과값을 살펴보겠습니다.
enum DirectionNumber {
UP,
DONW,
RIGHT,
LEFT,
}
enum DirectionString {
UP = "up",
DONW = "down",
RIGHT = "right",
LEFT = "left",
}
console.log(DirectionNumber);
/*
{
'0': 'UP',
'1': 'DONW',
'2': 'RIGHT',
'3': 'LEFT',
UP: 0,
DONW: 1,
RIGHT: 2,
LEFT: 3
}
*/
console.log(DirectionString);
// { UP: 'up', DONW: 'down', RIGHT: 'right', LEFT: 'left' }
숫자형 타입에는 값으로도 접근을 할수 있게끔 mapping이 되고 있는것을 확인할 수 있습니다.
console.log(DirectionNumber["0"]);
console.log(DirectionString["up"])
// UP
// undefined
이렇게 숫자형 타입에서 값으로도 접근할 수 있게 해주는 것을 reverse mapping이라고 합니다.
reverse mapping이 생성되는 이유는 transpile된 코드를 살펴보면 확인할 수 있습니다.
// reverse mapping 중
// DirectionNumber["UP"] = 0은 0을 반환함
var DirectionNumber;
(function (DirectionNumber) {
DirectionNumber[DirectionNumber["UP"] = 0] = "UP";
DirectionNumber[DirectionNumber["DONW"] = 1] = "DONW";
DirectionNumber[DirectionNumber["RIGHT"] = 2] = "RIGHT";
DirectionNumber[DirectionNumber["LEFT"] = 3] = "LEFT";
})(DirectionNumber || (DirectionNumber = {}));
var DirectionString;
(function (DirectionString) {
DirectionString["UP"] = "up";
DirectionString["DONW"] = "down";
DirectionString["RIGHT"] = "right";
DirectionString["LEFT"] = "left";
})(DirectionString || (DirectionString = {}));
잠깐 딴길로 새서 typescript에서 let과 const의 차이를 알아보겠습니다.
원시값의 타입과 참조값의 타입의 경우를 살펴보겠습니다.
원시 타입
let 변수의 경우 value의 타입이 나타나는 것을 볼 수 있습니다.
여기에 as const를 붙이면 어떻게 될까요?
타입이 "say hello"로 const에서의 경우와 같게끔 나오는 것을 확인할 수 있습니다.
원래 let 변수에는 재할당이 가능합니다.
하지만 as const를 사용함으로써 재할당이 불가능하게 되었습니다.
참조 타입
객체를 const로 선언한 후에 타입을 살펴 보겠습니다.
const로 선언하였지만 value의 타입들이 mapping된 것을 확인할 수 있습니다.
이렇게 되면 추후에 값을 변경할 수가 있게 됩니다.
const로 선언한 객체에도 값을 변환할 수 없게 할 수 있습니다.
as const를 붙여주어 readonly가 추가된 것을 확인할 수 있습니다.
이렇게 되면 test 변수의 값을 변경할 수가 없게 됩니다.
이 as const를 이용하여 enum을 대신할 수 있습니다.
const enum, enum, as const
자 이제 거의 다왔습니다.
enum DirectionNumber {
UP,
DONW,
RIGHT,
LEFT,
}
const DirectionObject = {
UP: 0,
DOWN: 1,
RIGHT: 2,
LEFT: 3
} as const
console.log(DirectionNumber.UP); // 0
console.log(DirectionObject["UP"]); // 0
위의 경우 두개의 출력값은 같고 DirectionObject는 readonly 속성을 가지게 됩니다.
이제 object를 enum 대신 사용할수 있을것만 같습니다.
아래와 같이 속성을 추가할 수도 있습니다.
const DirectionObject = {
UP: 0,
DOWN: 1,
RIGHT: 2,
LEFT: 3
}
DirectionObject["CENTER"] = 4
Object.defineProperty(DirectionObject, "CENTER", { value: 4, writable: false, enumerable: true})
DirectionObject["CENTER"] = 5
예제 코드에서는 writable: false를 사용하여 값의 재할당이 불가능하게 하였습니다.
위의 예제에 as const를 사용하면 더 역할을 잘 수행할 수 있을것 같습니다.
하지만 객체가 enum의 역할을 수행하는 것은 코드를 접했을때 enum 보다는 확실한 메시지를 전달할 수 없습니다.
나는 열거형 타입이야 라고 외치고 있는 enum을 사용하는 것이 코드의 의미를 파악하기 더 쉽지 않을까요??!!
하지만 enum은 객체에 비해 transpile되는 코드량이 많고 reverse mapping의 경우 불필요한 코드의 생산을 초래할 수 있습니다.
숫자형 열거 타입에서 값으로 접근하는 경우는 특수한 경우와 용도가 아니라면 사용되지 않을것 같습니다.(Log에 error 찍는 경우???)
근데 왜 불필요한 코드가 적어야 할까요??
사용자의 디바이스에서 transpile된 코드를 실행하게 됩니다.
불필요한 코드가 줄어들면 파일의 크기가 줄어들고 실행이 간결해 집니다.
사용자는 로드를 더 빠르게 할 수 있고 화면에 렌더링이 더 빠르게 할 수 있다라는 뜻이 됩니다.
이런 문제를 해결할 수 있는 것이 enum앞에 const를 붙이는 것입니다.
위의 이미지는 enum을 const로 선언한 것과 하지 않은것의 transpile된 결과 입니다.
const enum을 transpile하는 코드는 존재하지 않고 값만 출력하고 있는 것을 볼 수 있습니다.
자 이제 어떤걸 사용해야 하는지 알수 있을것 같습니다.
enum을 사용하여 좀 더 의미있는 코드를 작성합니다.
근데 이때 const를 곁들이면 transpile 된 코드의 양을 줄일수 있습니다!!
Object와 enum의 차이
- object 는 코드내에서 새로운 속성을 자유롭게 추가할 수 있지만, enum 은 선언할때 이후에 변경할 수 없습니다.
- object 의 속성값은 JS가 허용하는 모든 타입이 올 수 있지만, enum 의 속성값으로는 문자열 혹은 숫자만 허용됩니다.
출처:
https://www.typescriptlang.org/docs/handbook/enums.html#computed-and-constant-members
https://sambalim.tistory.com/142
https://velog.io/@logqwerty/Enum-vs-as-const
'Frontend > Typescript' 카테고리의 다른 글
Typescript class 🏛 (0) | 2022.04.19 |
---|---|
Typescript 사용 이유 (0) | 2022.03.23 |
Typescript type vs interface (0) | 2022.03.23 |
Typescript 유틸리티 타입 [Utility Types] (0) | 2022.03.02 |
Typescript '<variable>' is declared but its value is never read. (0) | 2022.03.02 |