W języku TypeScript można zdefiniować wyliczenie na kilka różnych sposobów:
enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }
Jeśli spróbuję użyć wartości z Gamma
w czasie wykonywania, Gamma
pojawia się błąd, ponieważ nie została zdefiniowana, ale tak nie jest w przypadku Delta
lub Alpha
? Co oznacza const
lub declare
oznacza w deklaracjach tutaj?
Jest też preserveConstEnums
flaga kompilatora - jak to współdziała z nimi?
enums
typescript
Ryan Cavanaugh
źródło
źródło
Odpowiedzi:
Istnieją cztery różne aspekty wyliczeń w języku TypeScript, o których należy pamiętać. Najpierw kilka definicji:
„obiekt wyszukiwania”
Jeśli napiszesz to wyliczenie:
TypeScript wyemituje następujący obiekt:
Nazywam to obiektem wyszukiwania . Jego cel jest dwojaki: służy jako odwzorowanie ciągów znaków na liczby , np. Podczas pisania
Foo.X
lubFoo['X']
, oraz służy jako odwzorowanie liczb na łańcuchy . To odwrotne odwzorowanie jest przydatne do debugowania lub rejestrowania - często będziesz mieć wartość0
lub1
i chcesz uzyskać odpowiedni ciąg"X"
lub"Y"
.„deklaracja” lub „ otoczenie ”
W języku TypeScript można „zadeklarować” rzeczy, o których kompilator powinien wiedzieć, ale w rzeczywistości nie emitować kodu. Jest to przydatne, gdy masz biblioteki, takie jak jQuery, które definiują obiekt (np.
$
), O którym chcesz wpisać informacje, ale nie potrzebujesz żadnego kodu utworzonego przez kompilator. Specyfikacja i inna dokumentacja odnosi się do deklaracji sporządzonych w ten sposób jako znajdujących się w kontekście „otoczenia”; należy zauważyć, że wszystkie deklaracje w.d.ts
pliku są „otoczone” (albo wymagają jawnegodeclare
modyfikatora, albo mają go niejawnie, w zależności od typu deklaracji).„inlining”
Ze względu na wydajność i rozmiar kodu często preferowane jest zastąpienie odniesienia do elementu członkowskiego wyliczenia jego liczbowym odpowiednikiem podczas kompilacji:
Specyfikacja nazywa to zastąpienie , nazwę to inlining, ponieważ brzmi fajniej. Czasami nie chcesz, aby elementy członkowskie wyliczenia były wstawiane, na przykład ponieważ wartość wyliczenia może ulec zmianie w przyszłej wersji interfejsu API.
Enum, jak to działa?
Podzielmy to na każdy aspekt wyliczenia. Niestety, każda z tych czterech sekcji będzie odnosić się do terminów ze wszystkich pozostałych, więc prawdopodobnie będziesz musiał przeczytać tę całość więcej niż raz.
obliczone a nieobliczone (stała)
Składowe wyliczenia mogą być obliczane lub nie. Specyfikacja wywołuje nieobliczone elementy składowe stałymi , ale będę je nazywać nieobliczonymi, aby uniknąć pomyłki z const .
Obliczane członkiem enum jest ten, którego wartość nie jest znany w czasie kompilacji. Oczywiście nie można wstawiać odwołań do obliczonych członków. Odwrotnie, niż wyliczona element wyliczenia jest po których wartość jest znana w czasie kompilacji. Odwołania do nieobliczonych elementów członkowskich są zawsze wstawiane.
Które elementy członkowskie wyliczenia są obliczane, a które nie? Po pierwsze, wszystkie elementy
const
wyliczenia są stałe (tj. Nieobliczane), jak sama nazwa wskazuje. W przypadku wyliczenia innego niż stała, zależy to od tego, czy patrzysz na wyliczenie otoczenia (deklarowanie), czy wyliczenie inne niż otoczenia.Element członkowski
declare enum
(tj. Wyliczenie otoczenia) jest stały wtedy i tylko wtedy, gdy ma inicjator. W przeciwnym razie jest obliczany. Zauważ, że w adeclare enum
dozwolone są tylko inicjatory numeryczne. Przykład:Na koniec, elementy składowe niezadeklarowanych wyliczeń innych niż stałe są zawsze traktowane jako obliczone. Jednak ich wyrażenia inicjujące są zredukowane do stałych, jeśli są obliczalne w czasie kompilacji. Oznacza to, że nie będące stałymi składowymi wyliczenia nigdy nie są wstawiane (to zachowanie zostało zmienione w języku TypeScript 1.5, zobacz „Zmiany w języku TypeScript” u dołu)
const vs non-const
konst
Deklaracja wyliczenia może mieć
const
modyfikator. Jeśli wyliczenie toconst
, wszystkie odwołania do jego członków są wstawione.Wyliczenia const nie generują obiektu wyszukiwania podczas kompilacji. Z tego powodu błędem jest odwoływanie się
Foo
do powyższego kodu, z wyjątkiem odniesienia do elementu członkowskiego. ŻadenFoo
obiekt nie będzie obecny w czasie wykonywania.non-const
Jeśli deklaracja wyliczenia nie ma
const
modyfikatora, odwołania do jej elementów członkowskich są wstawiane tylko wtedy, gdy element członkowski nie jest obliczany. Wyliczenie inne niż stała, niezadeklarowane spowoduje utworzenie obiektu wyszukiwania.deklarować (otoczenia) vs deklarować
Ważnym wstępem jest to, że
declare
w TypeScript ma bardzo specyficzne znaczenie: ten obiekt istnieje gdzie indziej . Służy do opisywania istniejących obiektów. Używaniedeclare
do definiowania obiektów, które w rzeczywistości nie istnieją, może mieć złe konsekwencje; zbadamy je później.ogłosić
A
declare enum
nie wyemituje obiektu wyszukiwania. Odwołania do jego elementów członkowskich są wstawiane, jeśli te elementy członkowskie są obliczane (patrz powyżej na temat obliczonych i nieobliczonych).Należy zauważyć, że dozwolone
declare enum
są inne formy odwołań do a , np. Ten kod nie jest błędem kompilacji, ale zakończy się niepowodzeniem w czasie wykonywania:Ten błąd należy do kategorii „Nie kłam kompilatorowi”. Jeśli nie masz obiektu nazwanego
Foo
w czasie wykonywania, nie piszdeclare enum Foo
!A
declare const enum
nie różni się od aconst enum
, z wyjątkiem przypadku --preserveConstEnums (patrz poniżej).nie deklarować
Niezadeklarowane wyliczenie tworzy obiekt wyszukiwania, jeśli nie jest
const
. Inlining opisano powyżej.--preserveConstEnums flaga
Ta flaga ma dokładnie jeden efekt: niezadeklarowane wyliczenia const będą emitować obiekt wyszukiwania. Nie ma to wpływu na podszewkę. Jest to przydatne do debugowania.
Powszechne błędy
Najczęstszym błędem jest użycie a,
declare enum
gdy zwykłyenum
lubconst enum
byłby bardziej odpowiedni. Typowa forma jest taka:Pamiętaj o złotej zasadzie: nigdy
declare
rzeczy, które nie istnieją . Użyj,const enum
jeśli chcesz zawsze wstawiać lubenum
jeśli chcesz, aby obiekt wyszukiwania.Zmiany w TypeScript
Pomiędzy TypeScript 1.4 i 1.5 nastąpiła zmiana w zachowaniu (patrz https://github.com/Microsoft/TypeScript/issues/2183 ), aby wszystkie składowe niezadeklarowanych wyliczeń innych niż const były traktowane jako obliczone, nawet jeśli są jawnie zainicjowane literałem. To, że tak powiem, „rozłupać dziecko”, czyniąc zachowanie inlining bardziej przewidywalnym i wyraźniej oddzielając koncepcję
const enum
od zwykłejenum
. Przed tą zmianą nieobliczane elementy składowe wyliczeń innych niż const były wstawiane bardziej agresywnie.źródło
enum
typów, dziękuję!const
dla zadeklarowanych typów wyliczeń.Dzieje się tu kilka rzeczy. Chodźmy na każdy przypadek.
enum
Po pierwsze, zwykły stary wyliczenie. Po skompilowaniu do JavaScript wyemituje to tablicę przeglądową.
Tabela przeglądowa wygląda następująco:
Następnie, gdy masz
Cheese.Brie
w TypeScript, emitujeCheese.Brie
w JavaScript, który szacuje na 0.Cheese[0]
EmitujeCheese[0]
i faktycznie ocenia do"Brie"
.const enum
W rzeczywistości żaden kod nie jest do tego emitowany! Jego wartości są zapisane. Poniższe emitują samą wartość 0 w JavaScript:
const enum
s 'inlining może być przydatne ze względu na wydajność.Ale co z tym
Bread[0]
? Spowoduje to błąd w czasie wykonywania i Twój kompilator powinien go złapać. Nie ma tabeli wyszukiwania, a kompilator nie jest tutaj wbudowany.Zauważ, że w powyższym przypadku flaga --preserveConstEnums spowoduje, że Bread wyemituje tablicę przeglądową. Jednak jego wartości będą nadal zapisane.
zadeklaruj wyliczenie
Podobnie jak w przypadku innych zastosowań programu
declare
,declare
nie emituje żadnego kodu i oczekuje, że właściwy kod zdefiniujesz w innym miejscu. To nie emituje tabeli przeglądowej:Wine.Red
emitujeWine.Red
w JavaScript, ale nie będzie żadnej tabeli wyszukiwania Wine, do której można by się odwołać, więc jest to błąd, chyba że zdefiniowałeś go gdzie indziej.deklaruj const enum
To nie emituje tabeli przeglądowej:
Ale działa w linii!
Fruit.Apple
emituje 0. Ale znowuFruit[0]
wyświetli się błąd w czasie wykonywania, ponieważ nie jest wstawiony i nie ma tabeli odnośników.Napisałem to na tym placu zabaw. Polecam grać tam, aby zrozumieć, który TypeScript emituje który JavaScript.
źródło
Bread[0]
zgłasza błąd kompilatora: „Do elementu członkowskiego const enum można uzyskać dostęp tylko za pomocą literału ciągu”.