TypeScript에는 열거 형을 정의하는 여러 가지 방법이 있습니다.
enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }
Gamma
런타임에 값을 사용하려고하면 Gamma
이 정의되지 않았기 때문에 오류가 발생 하지만 그렇지 않은 경우 Delta
또는 Alpha
? 무엇 않습니다 const
또는 declare
여기 선언에 뜻?
preserveConstEnums
컴파일러 플래그 도 있습니다. 이것이 이것들과 어떻게 상호 작용합니까?
답변
주의해야 할 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 열거 형의 계산되지 않은 멤버가 더 적극적으로 인라인되었습니다.
답변
여기에는 몇 가지 일이 있습니다. 사례별로 가자.
열거 형
enum Cheese { Brie, Cheddar }
첫째, 평범한 오래된 열거 형. JavaScript로 컴파일하면 조회 테이블이 생성됩니다.
조회 테이블은 다음과 같습니다.
var Cheese;
(function (Cheese) {
Cheese[Cheese["Brie"] = 0] = "Brie";
Cheese[Cheese["Cheddar"] = 1] = "Cheddar";
})(Cheese || (Cheese = {}));
그런 다음 Cheese.Brie
TypeScript에 있으면 Cheese.Brie
0으로 평가되는 JavaScript에서 Cheese[0]
방출 됩니다. 방출 Cheese[0]
하고 실제로 평가합니다."Brie"
.
const 열거 형
const enum Bread { Rye, Wheat }
이를 위해 실제로 코드가 생성되지 않습니다! 값은 인라인됩니다. 다음은 JavaScript에서 값 0 자체를 내 보냅니다.
Bread.Rye
Bread['Rye']
const enum
s ‘인라인은 성능상의 이유로 유용 할 수 있습니다.
하지만 Bread[0]
어떨까요? 이것은 런타임에 오류가 발생하고 컴파일러가이를 포착해야합니다. 조회 테이블이없고 컴파일러가 여기에 인라인되지 않습니다.
위의 경우 –preserveConstEnums 플래그는 Bread가 조회 테이블을 내보내도록합니다. 그 값은 여전히 인라인됩니다.
열거 형 선언
의 다른 용도와 마찬가지로 declare
, declare
어떤 코드를 방출하지 않고 다른 곳에서 실제 코드를 정의한 것으로 기대하고있다. 이것은 조회 테이블을 생성하지 않습니다.
declare enum Wine { Red, Wine }
Wine.Red
방출 Wine.Red
JavaScript에서 하지만 참조 할 Wine 조회 테이블이 없으므로 다른 곳에서 정의하지 않는 한 오류입니다.
const enum 선언
이것은 조회 테이블을 생성하지 않습니다.
declare const enum Fruit { Apple, Pear }
그러나 그것은 인라인입니다! Fruit.Apple
0을 방출합니다. 그러나 Fruit[0]
인라인되지 않고 조회 테이블이 없기 때문에 런타임에 다시 오류가 발생합니다.
이 놀이터 에 이것을 적었습니다. 어떤 TypeScript가 어떤 JavaScript를 방출하는지 이해하기 위해 그곳에서 플레이하는 것이 좋습니다.