TypeScript Study Note

Ginie·2021년 1월 22일
0

TypeScript

목록 보기
10/11
post-thumbnail

열거형

  • 이름이 있는 상수들의 집합을 열거형으로 정의할 수 있다.
  • 의도를 문서화 하거나, 구분되는 사례 집합을 더 쉽게 만들 수 있다.
  • TS는 숫자와 문자열 기반 열거형을 제공한다.

숫자 열거형

  • 열거형은 enum 키워드를 사용해 정의할 수 있다.
enum Direction {
  Up = 1, // 1로 초기화된 숫자 열거형을 선언
  Down, // 뒤 따르는 멤버들은 자동으로 증가된 값을 갖는다. 2
  Left, // 3
  Right, // 4
}
// 전부 초기화 하지 않을 수도 있다.
enum Direction {
  Up, // 0
  Down, // 1
  Left, // 2
  Right,// 3 
}
  • 자동으로 증가하는 기능은 멤버 값 자체에는 신경쓰지 않지만, 각 값이 같은 열거형의 다른 값과 구별돼야 하는 경우에 유용하다.
  • 열거형 자체에서 프로퍼티로 모든 멤버에 접근하요, 열거형의 이름을 사용해 타입을 선언한다.
enum Response {
    No = 0,
    Yes = 1,
}

function respond(recipient: string, message: Response): void {
    // ...
}

respond("Princess Caroline", Response.Yes)
  • 숫자 열거형은 계산된 멤버와 상수 멤버를 섞어서 사용할 수 있다.
  • 초기화되지 않은 열거형이 먼저 나오거나, 숫자 상수, 다른 상수 열거형 멤버와 함께 초기회된 숫자 열거형 이휴에 나와야 한다.
enum E {
  A = getSomeValue(),
  B, // 오류 : 앞에 A가 계산된 멤버여서 초기화가 필요하다.

계산된 멤버와 상수 멤버
각 열거형의 멤버는 상수거나, 계산된 값일 수 있다.
상수로 간주하는 경우
enum E { X } 에서 E.X는 상수이다.
초기화 값이 없고, 숫자 상수로 초기회된 열거형 멤버 뒤에 따라나오는 경우 앞에 나온 상수 값에 1씩 증가한 값을 상수로 갖는다.

enum E1 { X, Y, Z }

enum E2 {
    A = 1, B, C
} // 'E1' 과 'E2' 의 모든 열거형 멤버는 상수입니다.

열거형 멤버는 상수 열거형 표현식으로 초기화된다.
상수 열거형 표현식은 컴파일 시 알아낼 수 있는 TS 표현식의 일부입니다.
상수 열거형 표현식
1. 리터럴 열거형 표현식 (기본적으로 문자 또는 숫자 리터럴)
2. 이전에 정의된 다른 상수 열거형 멤버에 대한 참조 (다른 열거형에서 시작될 수 있음)
3. 괄호로 묶인 상수 열거형 표현식
4. 상수 열거형 표현식에 단항 연산자 +, -, ~ 를 사용한 경우
5. 상수 열거형 표현식을 이중 연산자 +, -, *, /, %, <<, >>, >>>, &, |, ^ 의 피연산자로 사용할 경우
상수 열거형 표현식 값이 NaN 이거나 Infinity 이면 컴파일 시점에 오류가 난다.
이 외 다른 모든 경우 열거형 멤버는 계산된 것으로 간주한다.

enum FileAccess {
    // 상수 멤버
    None,
    Read    = 1 << 1,
    Write   = 1 << 2,
    ReadWrite  = Read | Write,
    // 계산된 멤버
    G = "123".length
}

문자열 열거형

  • 런타임에서 열거형의 동작이 다르다.
  • 각 멤버들은 문자열 리터럴 또는 다른 문자열 열거형의 멤버로 상수 초기화를 해야한다.
  • 자동 증가하는 기능은 없지만, 직렬화를 잘한다는 장점이 있다.
  • 열거형 멤버에 지정된 이름과는 무관하게 유의미하고 읽기 좋은 값을 이용해서 실행할 수 있다.
enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

런타임에서 열거형
열거형은 런타임에 존재하는 실제 객체이다.

enum E {
  X, Y, Z
}
// 실제로 아래와 같이 함수로 전달될 수 있다.
function f(obj: { X: number }) {
  return obj.X;
}
// E가 X라는 숫자 프로퍼티를 가지고 있기 떄문에 동작하는 코드이다.
f(E);

이종 열거형
기술적으로 문자열과 숫자열 열거형을 섞을수 있지만 굳이 그렇기 할 이유가 없다.
JS 런타임에서 장점을 취하려 것이 아니라면, 권장 안한다.

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}

유니언 열거형과 열거형 멤버 타입

  • 리터럴 열거형 멤버 리터럴 열거형 멤버는 초기화 값이 존재하지 않거나, 아래 값들로 초기화되는 멤버입니다.
    • 문자 리터럴 ("foo", "bar, "baz")
    • 숫자 리터럴 (1, 100)
    • 숫자 리터럴에 단항 연산자 -가 적용된 경우 (-1, -100)
  • 열거형의 모든 멤버가 리터럴 열거형 값을 가지면 특별한 의미로 쓰이게 된다.
  1. 열거형 멤버를 타입처럼 사용할 수 있다.
enum ShapeKind {
  Circle,
  Square,
}

interface Circle {
  kind: ShapeKind.Circle;
  radius: number;
}

interface Square {
  kind: ShapeKind.Square;
  sideLength: number;
}

let c: Circle = {
  kind: ShapeKind.Square, // ShapeKind.Square 형식은 ShapeKind.Circle 형식에 할당할 수 없다.
  radius: 100,
}
  1. 열거형 타입 자체가 효율적으로 각각의 열거형 멤버의 유니언이 된 다는 점!
    유니언 타입 열거형을 사용하면 값을 잘못 비교하는 에러를 방지할 수 있다.
    (타입 시스템이 열거형 자체에 존재하는 정확한 값의 집합을 알고 있기 때문이다.)
enum E {
    Foo,
    Bar,
}

function f(x: E) {
    if (x !== E.Foo || x !== E.Bar) { 
      // x가 E.Foo가 아닌지 확인한다. true면? || 이후 조건은 체크 할 필요가 없음 
      // 만약 조건이 false면? x는 E.Foo이기 때문에 E.Bar가 아닌지 묻는것은 적절하지 않다.
      // 에러! E 타입은 Foo, Bar 둘 중 하나이기 때문에 이 조건은 항상 true를 반환합니다.
    }
}

컴파일 시점에서 열거형

  • keyof typeof 키워드를 사용하면 모든 열거형의 키를 문자열로 나타내는 타입을 가져온다.
enum LogLevel {
    ERROR, WARN, INFO, DEBUG
}

type LogLevelStrings = keyof typeof LogLevel;

function printImportant(key: LogLevelStrings, messsage: string) {
    const num = LogLevel[key];
    if (num <= LogLevel.WARN) {
        console.log('Log level key is :', key);
        console.log('Log level value is : ', num);
        console.log('Log level messsage is : ', messsage);
    }
}
printImportant('ERROR', 'This is a messsage');

역 매핑

  • 숫자 열거형 멤버는 멤버의 프로퍼티 이름을 가지 객체를 생성하는 것 외에도 열거형 값에서 열거형 이름으로 역 매핑을 받는다.
enum Enum {
    A
}

let a = Enum.A;
let nameOfA = Enum[0];
// 👆 이 예제는 JS코드로 컴파일 하면 
var Enum;
(function (Enum) {
    Enum[Enum["A"] = 0] = "A";
})(Enum || (Enum = {}));
var a = Enum.A;
var nameOfA = Enum[0];
// 👆 이렇게 컴파일 된다.
  • 열거형은 정방향(name -> value) 매핑과 역방향(value -> name)매핑 두 정보를 모두 저장하는 객체로 컴파일 된다. 다른 열거형 멤버 참조는 항상 프로퍼티 접근으로 노출 되고 인라인되지 않는다.
  • 문자열 열거형은 역 매핑을 생성하지 않는다.

const 열거형

  • 열거형 값에 접근할 때 추가로 생성된 코드 및 추가적인 간접 참조에 대한 비용을 피하기 위해 사용한다.
const enum Enum {
    A = 1,
    B = A * 2
}
  • 상수 열거형 표현식만 가능하다.
  • 컴파일 과정에서 완전히 제거가 됨.
  • 사용하는 공간에 인라인 된다. (계산된 멤버를 가지고 있지 않기 때문이다.)
const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let direction = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
// 위의 ts 파일은 아래 js 파일로 컴파일 된다.
var direction = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

Ambient(주변) 열거형

  • 이미 존재하는 열거형 타입의 모습을 묘사하기 위해 사용된다.
declare enum Enum {
    A = 1,
    B,
    C = 2
}

ambient 열거형 VS 비-ambient 열거형
비-ambient 열거형: 초기화되지 않은 멤버가 상수로 간주하는 멤버 뒤에 있다면, 이 멤버도 상수로 간주한다.
ambient 열거형: 초기화되지 않은 멤버는 항상 계산된 멤버로 간주한다.

profile
느리지만 꾸준한 프론트엔드 주니어 개발자 🐢

0개의 댓글