주의해야 할 TypeScript의 열거 형에는 네 가지 측면이 있습니다. 첫째, 몇 가지 정의 :
"조회 개체"
이 열거 형을 작성하면 :
enum Foo { X, Y }
TypeScript는 다음 개체를 내 보냅니다.
var Foo;
(function (Foo) {
Foo[Foo["X"] = 0] = "X";
Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));
이것을 조회 객체라고 부를 것 입니다. 그 목적은 두 가지이다 :에서 매핑 역할을 문자열 로 번호를 기록 할 때 예를 들어, Foo.X
나 Foo['X']
, 그리고에서 매핑 역할을 숫자 로 문자열 . 이 역 매핑은 디버깅 또는 로깅 목적에 유용합니다. 종종 값이 0
있거나 1
해당 문자열 "X"
또는 "Y"
.
"선언" 또는 " 주변 "
TypeScript에서는 컴파일러가 알아야하는 것을 "선언"할 수 있지만 실제로 코드를 내보내지는 않습니다. 이것은 일부 객체를 정의하는 jQuery와 같은 라이브러리가있을 때 유용합니다 (예 :$
타입 정보를 원하지만 컴파일러에 의해 생성 된 코드가 필요하지 않은 . 사양 및 기타 문서에서는 이러한 방식으로 작성된 선언을 "주변"컨텍스트에있는 것으로 언급합니다. .d.ts
파일의 모든 선언 이 "주변" 이라는 점에 유의하는 것이 중요합니다 ( declare
선언 유형에 따라 명시 적 수정자가 필요 하거나 암시 적으로 포함됨).
"인라이닝"
성능 및 코드 크기 때문에 컴파일 할 때 열거 형 멤버에 대한 참조를 해당 숫자로 대체하는 것이 선호되는 경우가 많습니다.
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
사양에서는이 대체 라고 부르며, 더 멋지게 들리기 때문에 인라인 이라고합니다. 열거 형 멤버가 인라인되는 것을 원하지 않는 경우가 있습니다. 예를 들어 향후 API 버전에서 열거 형 값이 변경 될 수 있기 때문입니다.
열거 형, 어떻게 작동합니까?
열거 형의 각 측면으로 이것을 분해 해 보겠습니다. 안타깝게도이 네 섹션 각각은 다른 모든 섹션의 용어를 참조하므로이 전체를 한 번 이상 읽어야 할 것입니다.
계산 됨 vs 계산되지 않음 (상수)
열거 형 멤버는 계산 되거나 계산 되지 않을 수 있습니다. 사양은 계산되지 않은 멤버 상수를 호출하지만 const 와의 혼동을 피하기 위해 계산되지 않은 멤버 라고 부를 것입니다. .
계산 ENUM 부재는 그 값이 컴파일시에 알려져 있지 하나이다. 물론 계산 된 멤버에 대한 참조는 인라인 될 수 없습니다. 반대로 계산되지 않은 열거 형 멤버는 값 이 컴파일 타임에 알려진 . 계산되지 않은 멤버에 대한 참조는 항상 인라인됩니다.
계산되는 열거 형 멤버와 계산되지 않는 멤버는 무엇입니까? 첫째, const
열거 형 의 모든 멤버 는 이름에서 알 수 있듯이 상수 (즉, 계산되지 않음)입니다. 상수가 아닌 열거 형의 경우 주변 (선언) 열거 형을 보고 있는지 아니면 주변 이 아닌 열거 형을 보고 있는지에 따라 다릅니다 .
a의 멤버 declare enum
(즉, 앰비언트 열거 형)는 이니셜 라이저가있는 경우에만 상수 입니다. 그렇지 않으면 계산됩니다. A의 참고 declare enum
숫자 만 초기화가 허용됩니다. 예:
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
마지막으로 비 선언 non-const 열거 형의 멤버는 항상 계산 된 것으로 간주됩니다. 그러나 초기화 표현식은 컴파일 타임에 계산할 수있는 경우 상수로 축소됩니다. 즉, 상수가 아닌 열거 형 멤버는 인라인되지 않습니다 (이 동작은 TypeScript 1.5에서 변경되었습니다. 맨 아래의 "TypeScript의 변경 사항"참조).
const와 non-const
const
열거 형 선언에는 const
수정자가 있을 수 있습니다 . 열거 형이 const
인 경우 해당 멤버에 대한 모든 참조가 인라인됩니다.
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
const 열거 형은 컴파일 될 때 조회 개체를 생성하지 않습니다. Foo
따라서 멤버 참조의 일부를 제외하고 위 코드에서 참조하는 것은 오류 입니다. 아니Foo
런타임에는 개체가 .
상수가 아닌
열거 형 선언에 const
수정자가 없으면 해당 멤버에 대한 참조는 멤버가 계산되지 않은 경우에만 인라인됩니다. 상수가 아닌 선언되지 않은 열거 형은 조회 개체를 생성합니다.
선언 (주변) 대 비 선언
중요한 서문은 declare
TypeScript에서 매우 구체적인 의미 가 있다는 것 입니다. 이 객체는 다른 곳에 존재합니다 . 기존 객체 를 설명하기위한 것 입니다. declare
실제로 존재하지 않는 객체를 정의하는 데 사용하면 나쁜 결과를 초래할 수 있습니다. 나중에 살펴 보겠습니다.
알리다
ㅏ declare enum
는 조회 개체를 내 보내지 않습니다. 해당 멤버가 계산 된 경우 해당 멤버에 대한 참조가 인라인됩니다 (계산 및 비계산에 대한 위 참조).
그것은 참조의 다른 형태에 유의해야 declare enum
된다 , 예를 들어,이 코드는 허용 되지 컴파일 오류 만 합니다 런타임에 실패 :
// Note: Assume no other file has actually created a Foo var at runtime
declare enum Foo { Bar }
var s = 'Bar';
var b = Foo[s]; // Fails
이 오류는 "컴파일러에게 거짓말을하지 마십시오"범주에 속합니다. Foo
런타임에 이름이 지정된 객체 가 없으면 작성하지 마십시오 declare enum Foo
!
A declare const enum
는 const enum
--preserveConstEnums의 경우를 제외하고는 a 와 다르지 않습니다 (아래 참조).
비 선언
선언되지 않은 열거 형은 그렇지 않은 경우 조회 개체를 생성합니다 const
. 인라이닝은 위에 설명되어 있습니다.
--preserveConstEnums 플래그
이 플래그는 정확히 한 가지 효과가 있습니다. 선언되지 않은 const 열거 형은 조회 객체를 내 보냅니다. 인라이닝은 영향을받지 않습니다. 이것은 디버깅에 유용합니다.
일반적인 오류
가장 일반적인 실수는 사용하는 declare enum
경우 정기적으로 enum
또는 const enum
더 적합 할 것입니다. 일반적인 형식은 다음과 같습니다.
module MyModule {
// Claiming this enum exists with 'declare', but it doesn't...
export declare enum Lies {
Foo = 0,
Bar = 1
}
var x = Lies.Foo; // Depend on inlining
}
module SomeOtherCode {
// x ends up as 'undefined' at runtime
import x = MyModule.Lies;
// Try to use lookup object, which ought to exist
// runtime error, canot read property 0 of undefined
console.log(x[x.Foo]);
}
황금률을 기억하십시오 : 실제로 존재하지 않는 것은 절대로하지 마십시오declare
. 사용 const enum
당신은 항상 인라인하려는 경우, 또는 enum
당신은 조회 오브젝트를 원하는 경우.
TypeScript의 변경 사항
TypeScript 1.4와 1.5 사이 에 비 선언 비 상수 열거 형의 모든 멤버가 다음과 같은 경우에도 계산 된 것으로 처리되도록 동작 ( https://github.com/Microsoft/TypeScript/issues/2183 참조 ) 이 변경되었습니다. 리터럴로 명시 적으로 초기화됩니다. 즉,이 "아기 분리"는 인라인 동작을보다 예측 가능하게 만들고 const enum
일반 에서 개념을보다 명확하게 분리합니다 enum
. 이 변경 이전에는 non-const 열거 형의 계산되지 않은 멤버가 더 적극적으로 인라인되었습니다.