열거형(enum)
열거형(enum)은 해당 타입으로 사용할 수 있는 값을 열거하는 기법입니다.
열거형은 키를 값에 할당하고 순서가 없는 자료구조입니다.
따라서 TypeScript는 키에 접근할 때 주어진 키가 실제 존재하는지 확인할 수 있습니다.
문자열에서 문자열로 매핑하거나 문자열에서 숫자로 매핑하는 열거형, 이렇게 두 가지가 있습니다.
아래의 예시는 열거형의 예입니다.
enum Language {
English,
Spanish,
Korean
}
열거형의 이름은 단수 명사로 쓰고, 첫 문자는 대문자로 하는 것이 관례입니다. 키도 앞 글자를 대문자로 표시합니다.
TypeScript는 자동적으로 열거형의 각 멤버에 적절한 숫자를 추론해 할당하지만, 값을 명시적으로 설정할 수도 있습니다.
TypeScript가 추론하게 했던 위의 예시를 명시하는 방식으로 바꿔보면 아래와 같습니다.
enum Language = {
English = 0,
Spanish = 1,
Korean = 2
}
보통의 객체에서 값을 가져올 때처럼, 점 또는 괄호 표기법으로 열거형 값에 접근할 수 있습니다.
let firstLanguage = Language.Spanish // Language
let secondLanguage = Language['English'] // Language
열거형을 여러 개로 나누어 정의한 다음 TypeScript가 이들을 합치도록 할 수도 있습니다.
단, TypeScript는 여러 열거형 정의 중 한 가지 값만 추론할 수 있으므로 열거형을 분리할 때 주의해야 하며, 아래처럼 각 열거형 멤버에 명시적으로 값을 할당하는 습관을 기르는 것이 좋습니다.
enum Language {
English = 0,
Spanish = 1
}
enum Language {
Korean = 2
}
계산된 값을 사용할 수도 있으므로 모든 값을 정의할 필요는 없습니다. 빠진 값은 TypeScript가 추론하기 때문입니다.
enum Language {
English = 100,
Spanish = 200 + 400,
Korean // TypeScript가 600 다음 숫자인 601로 추론
}
열거형에 문자열 값을 사용하거나 문자열과 숫자 값을 혼합할 수 있습니다.
enum Color {
Red = '#c10000',
Blue = '#0077ac1',
Pink = 0xc10050, // 16진수 리터럴
White = 255 // 10진수 리터럴
}
let red = Color.Red // Color
let pink = Color.Pink // Color
TypeScript에선 값이나 키로 열거형에 접근할 수 있도록 허용하지만, 이는 불안정한 결과를 초래하기 쉽습니다.
let a = Color.Red // Color
let b = Color.SkyBlue // error TS2339: 'SkyBlue' 프로퍼티는 'typeof Color' 타입에 존재하지 않음
let c = Color[255] // string
let d = Color[6] // string
Color[6]은 접근할 수 없어야 하지만 TypeScript는 접근을 허용합니다.
더 안전한 열거형 타입인 const enum을 이용하면, TypeScript가 이러한 안전하지 않은 작업을 막도록 만들 수 있습니다.
위의 예시를 더 안전하게 정의하면, 아래와 같습니다.
const enum Language {
English,
Spanish,
Korean
}
// 유효한 enum 키 접근
let a = Language.English // Language
// 유효하지 않은 enum 키 접근
let b = Language.Russian // error TS2339: 'Russian' 프로퍼티는 'typeof Language' 타입에 존재하지 않음
// 유효한 enum 키 접근
let c = Language[0] // error TS2476: const enum 멤버는 문자열 리터럴로만 접근할 수 있음
// 유효하지 않은 enum 키 접근
let d = Language[6] // error TS2467: const enum 멤버는 문자열 리터럴로만 접근할 수 있음
const enum은 역방향 찾기를 지원하지 않으므로 열거형의 동작은 일반 JavaScript 객체와 비슷해집니다.
또한 const enum은 기본적으로 아무 JavaScript도 생성하지 않으며, 그 대신 필요한 곳에 열거형 멤버의 값을 채워 넣습니다. (예를 들어 TypeScript는 Language.Spanish가 사용된 모든 코드를 값 1로 바꿉니다.)
TSC 플래그: preserveConstEnums
다른 TypeScript 코드에 정의된 const enum을 가져왔을 땐 이 채워 넣기 기능이 문제를 야기할 수 있습니다.
개발자가 TypeScript 코드를 컴파일한 이후에 열거형을 만든 사람이 자신의 const enum을 갱신하면, 런타임에 같은 열거형이 버전에 따라 다른 값을 갖게 되고, TypeScript가 이러한 상황에서 할 수 있는 일은 없습니다.
const enum을 사용할 때는 채워 넣기 기능을 되도록 피해야 하며, 제어할 수 있는 TypeScript 프로그램에서만 사용해야 합니다.
특히 NPM으로 배포하거나 라이브러리로 제공할 프로그램에선 const enum을 사용하지 말아야 합니다.
const enum의 런타임 코드 생성을 활성화하려면 tsconfig.json 파일에서 preserve ConstEnums TSC 설정을 true로 바꿉니다.
{
"compilerOptions": {
"preserveConstEnums": true
}
}
const enum 사용 방법
const enum Cafe {
Coffee,
Chair,
Cup,
Table,
Bread
}
function tomNtoms(t: Cafe){
return 'Welcome to tomNtoms'
}
tomNtoms(Cafe.Coffee) // 'Welcome to tomNtoms'
tomNtoms(Cafe.Table) // 'Welcome to tomNtoms'
tomNtoms(12) // 'Welcome to tomNtoms'
Coffee와 Table이 예상대로 동작하여 괜찮아 보이지만, 모든 숫자를 열거형에 할당할 수 있음을 알게 됩니다.
TypeScript의 할당 규칙 때문에 생긴 운 나쁜 결과물로, 해당 문제는 문자열을 갖는 열거형을 사용해 해결할 수 있습니다.
const enum Cafe {
Coffee = 'Coffee',
Chair = 'Chair',
Cup = 'Cup',
Table = 'Table',
Bread = 'Bread'
}
function tomNtoms(t: Cafe) {
return 'Welcome to tomNtoms'
}
tomNtoms(Cafe.Coffee) // Welcome to tomNtoms
tomNtoms(Cafe.Table) // Welcome to tomNtoms
tomNtoms(12) // error TS2345: '12'인수 타입은 'Cafe' 매개변수 타입에 할당할 수 없음
tomNtoms('person') // error TS2345: '"person"' 인수 타입은 'Cafe' 매개변수 타입에 할당할 수 없음
결과적으로 숫자 값을 받는 열거형은 전체 열거형의 안정성을 해칠 수 있습니다.
열거형을 안전하게 사용하는 방법은 솔직히 까다로우므로 열거형 자체를 멀리 할 것을 권한다고 합니다. TypeScript엔 열거형을 대체할 수단이 많습니다.
그래도 열거형 사용을 주장하는 주변 개발자가 있다면, 그 개발자가 화장실 갔을 때 숫자 값이나 const enum이 아닌 상황이거나 숫자 값을 받는 열거형임을 경고하도록 TSLint 규칙을 머지시켜 버리세요:)
'👶 TypeScript' 카테고리의 다른 글
선택적 매개변수와 기본 매개변수 (0) | 2023.01.10 |
---|---|
함수 선언과 호출 (0) | 2023.01.10 |
유니온과 인터섹션 타입 (0) | 2023.01.10 |
타입 별칭 (0) | 2023.01.09 |
타입 시스템 (0) | 2023.01.09 |