TypeScript'te farklı enum varyantları nasıl çalışır?


116

TypeScript, bir numaralandırmayı tanımlamak için birçok farklı yola sahiptir:

enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }

Çalışma Gammazamanında bir değer kullanmaya çalışırsam , bir hata alıyorum çünkü Gammatanımlı değil, ama bu Deltaveya Alpha? Buradaki beyanlar ne anlama geliyor constveya ne declareifade ediyor?

Ayrıca bir preserveConstEnumsderleyici bayrağı da var - bu bunlarla nasıl etkileşime giriyor?


1
Bu konuyla ilgili bir makale yazdım , bununla birlikte const ile sabit olmayan
enumların

Yanıtlar:


247

TypeScript'te bilmeniz gereken numaralandırmanın dört farklı yönü vardır. İlk olarak, bazı tanımlar:

"arama nesnesi"

Bu sıralamayı yazarsanız:

enum Foo { X, Y }

TypeScript aşağıdaki nesneyi yayınlayacaktır:

var Foo;
(function (Foo) {
    Foo[Foo["X"] = 0] = "X";
    Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));

Buna arama nesnesi olarak değineceğim . Onun iki amacı vardır: Bir eşleştirme olarak hizmet dizeleri için numaralar yazarken mesela, Foo.Xya da Foo['X'], ve bir eşleme olarak hizmet numaraları için dizeleri . Bu ters eşleme, hata ayıklama veya günlüğe kaydetme amaçları için kullanışlıdır - genellikle değere sahip olursunuz 0veya 1karşılık gelen dizeyi "X"veya "Y".

"bildirmek" veya " ortam "

TypeScript'te, derleyicinin bilmesi gereken şeyleri "bildirebilir", ancak aslında kod gönderemezsiniz. Bu, jQuery gibi $hakkında tür bilgisi istediğiniz, ancak derleyici tarafından oluşturulan herhangi bir koda ihtiyaç duymayan bazı nesneleri (örneğin ) tanımlayan kitaplıklarınız olduğunda kullanışlıdır . Spesifikasyon ve diğer belgeler, bir "ortam" bağlamında bu şekilde yapılan bildirimlere atıfta bulunur; Bir .d.tsdosyadaki tüm bildirimlerin "ambient" olduğuna dikkat etmek önemlidir ( declarebildirim türüne bağlı olarak, açık bir değiştirici gerektirir veya bunu örtük olarak içerir).

"İnlining"

Performans ve kod boyutu nedenleriyle, derlendiğinde bir numaralandırma üyesine referansın sayısal eşdeğeri ile değiştirilmesi genellikle tercih edilir:

enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";

Spesifikasyon bu ikameyi çağırıyor , ben buna satır içi diyeceğim çünkü daha havalı geliyor. Bazen olur değil sıralama değeri API gelecekteki bir sürümüne değişebilir çünkü enum üyeleri örneğin satır içine yerleştirilmiş istiyorum.


Enums, nasıl çalışıyorlar?

Bunu bir sıralamanın her bir yönüne göre ayıralım. Ne yazık ki, bu dört bölümün her biri diğerlerinin hepsinden terimlere başvuracak, bu yüzden muhtemelen tüm bunları birden fazla okumanız gerekecek.

hesaplanan ve hesaplanmayan (sabit)

Enum üyeleri hesaplanabilir veya hesaplanamaz . Spec olmayan bilgisayarlı üyeleri çağırır sabiti , ama onları arayacağım olmayan bilgisayarlı ile önlemek karışıklığa Kat .

Bir bilgisayarlı numaralama elemanı değeri derleme zamanında bilinen bir bileşiktir. Elbette, hesaplanan üyelere referanslar satır içi olamaz. Bunun aksine, bir sigara bilgisayarlı numaralama üyesidir değeri bir kez olan derleme zamanında da bilinir. Hesaplanmayan üyelere yapılan referanslar her zaman satır içi olarak yazılmıştır.

Hangi numaralandırma üyeleri hesaplanır ve hangileri hesaplanmaz? İlk olarak, bir constnumaralamanın tüm üyeleri , adından da anlaşılacağı gibi sabittir (yani hesaplanmamıştır). Sabit olmayan bir enum için, bir ambient (bildirim) enumuna mı yoksa ortam olmayan bir enum'a mı baktığınıza bağlıdır .

A'nın bir üyesi declare enum(yani, ortam sıralaması), ancak ve ancak bir başlatıcıya sahipse sabittir . Aksi takdirde hesaplanır. A'da declare enumyalnızca sayısal başlatıcılara izin verildiğini unutmayın. Misal:

declare enum Foo {
    X, // Computed
    Y = 2, // Non-computed
    Z, // Computed! Not 3! Careful!
    Q = 1 + 1 // Error
}

Son olarak, non-const enum'lerin üyeleri her zaman hesaplanmış olarak kabul edilir. Ancak, derleme zamanında hesaplanabilirlerse, başlangıç ​​ifadeleri sabitlere indirgenir. Bu, const olmayan enum üyelerinin hiçbir zaman satır içi olmadığı anlamına gelir (bu davranış TypeScript 1.5'te değişti, alttaki "TypeScript'teki Değişiklikler" konusuna bakın)

sabit ve sabit olmayan

const

Bir enum bildirimi constdeğiştiriciye sahip olabilir . Bir numaralandırma ise const, üyelerine yapılan tüm referanslar satır içinde.

const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always

const numaralandırmalar derlendiğinde bir arama nesnesi üretmez. Bu sebeple Fooüye referansının bir parçası olmadıkça yukarıdaki kodda referans verilmesi hatadır . Çalışma Foozamanında hiçbir nesne olmayacak.

const olmayan

Bir numaralandırma bildirimi constdeğiştiriciye sahip değilse , üyelerine yapılan başvurular yalnızca üye hesaplanmamışsa satır içi olur. Const olmayan, bildirim olmayan bir enum bir arama nesnesi oluşturur.

ilan et (ortam) vs beyan edilmeyen

Önemli bir önsöz, declareTypeScript'te çok özel bir anlama sahiptir: Bu nesne başka bir yerde var . Mevcut nesneleri tanımlamak içindir . declareGerçekte var olmayan nesneleri tanımlamak için kullanmanın kötü sonuçları olabilir; Bunları daha sonra keşfedeceğiz.

bildirmek

A declare enum, bir arama nesnesi yayınlamaz. Bu üyeler hesaplanmışsa üyelerine yapılan referanslar satır içi olarak yazılmıştır (bilgisayarlı ve hesaplanmayanlar için yukarıya bakın).

Bu bir referans o diğer formları unutmamak gerekir declare enum edilir , örneğin bu kodu izin değil bir derleme hatası ama olacak zamanında başarısız:

// 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

Bu hata "Derleyiciye yalan söylemeyin" kategorisine girer. Çalışma Foozamanında adlandırılmış bir nesneniz yoksa, yazmayın declare enum Foo!

A declare const enum, const enum--preserveConstEnums haricinde a'dan farklı değildir (aşağıya bakın).

olmayan beyan

Bildirilmemiş bir enum, değilse bir arama nesnesi üretir const. Satır içi yukarıda açıklanmıştır.

--preserveConstEnums bayrağı

Bu bayrağın tam olarak bir etkisi vardır: beyan edilmeyen sabit numaralandırmalar bir arama nesnesi yayınlar. Satır içi etkilenmez. Bu, hata ayıklama için kullanışlıdır.


Genel hatalar

En yaygın hata, declare enumnormal enumveya const enumdaha uygun olduğunda a kullanmaktır . Yaygın bir biçim şudur:

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]);
}

Altın kuralı hatırlayın: Gerçekte var olmayan şeyleri asladeclare . const enumHer zaman satır içi yapmak istiyorsanız veya enumarama nesnesini istiyorsanız kullanın .


TypeScript'teki değişiklikler

TypeScript 1.4 ve 1.5 arasında, sabit olmayan tanımlı olmayan numaralandırmaların tüm üyelerinin hesaplanmış olarak değerlendirilmesini sağlamak için davranışta bir değişiklik oldu (bkz. Https://github.com/Microsoft/TypeScript/issues/2183 ) bir değişmez ile açıkça başlatılırlar. Bu, tabiri caizse "bebeği ayırdı", satır içi davranışı daha öngörülebilir hale getiriyor ve kavramını const enumnormalden daha net bir şekilde ayırıyor enum. Bu değişiklikten önce, sabit olmayan numaralandırmaların hesaplanmamış üyeleri daha agresif bir şekilde satır içine alınmıştı.


6
Gerçekten harika bir cevap. Benim için pek çok şeyi açıklığa kavuşturdu, sadece numaralandırmalar değil.
Clark

1
Keşke sana birden fazla oy verebilseydim ... o değişiklikten haberim yoktu. Düzgün anlamsal sürümlemede bu, ana sürüme bir çarpma olarak düşünülebilir: - /
mfeineis

Çeşitli enumtürlerin çok yararlı bir karşılaştırması , teşekkürler!
Marius Schulz

@Ryan bu çok yardımcı oldu, teşekkürler! Şimdi , beyan edilen enum türlerine uygun olanı üretmek için Web Essentials 2015'e ihtiyacımız var const.
styfle

19
Bu yanıt, 1.4'teki bir durumu açıklamak için büyük ayrıntılara giriyor gibi görünüyor ve en sonunda "ama 1.5 tüm bunları değiştirdi ve şimdi çok daha basit" diyor. Her şeyi doğru anladığımı varsayarsak, bu cevap eskidikçe bu organizasyon gittikçe daha uygunsuz hale gelecektir : Daha basit, mevcut durumu ilk sıraya koymanızı ve ancak bundan sonra "ama eğer 1.4 veya daha eski kullanıyorsanız, biraz daha karmaşık. "
KRyan

33

Burada olan birkaç şey var. Hadi duruma göre gidelim.

Sıralama

enum Cheese { Brie, Cheddar }

İlk olarak, düz eski bir enum. JavaScript'e derlendiğinde, bu bir arama tablosu çıkarır.

Arama tablosu şuna benzer:

var Cheese;
(function (Cheese) {
    Cheese[Cheese["Brie"] = 0] = "Brie";
    Cheese[Cheese["Cheddar"] = 1] = "Cheddar";
})(Cheese || (Cheese = {}));

Daha sonra Cheese.BrieTypeScript'e sahip olduğunuzda Cheese.Brie, JavaScript'te Cheese[0]yayar Cheese[0]ve 0 olarak değerlendirilir ve gerçekte olarak değerlendirilir "Brie".

sabit enum

const enum Bread { Rye, Wheat }

Bunun için aslında hiçbir kod gönderilmez! Değerleri satır içi. Aşağıdaki, 0 değerini JavaScript'te kendisi yayar:

Bread.Rye
Bread['Rye']

const enums 'inlineing, performans nedenleriyle yararlı olabilir.

Peki ya ne olacak Bread[0]? Bu çalışma zamanında hata verecektir ve derleyiciniz onu yakalamalıdır. Arama tablosu yok ve derleyici burada satır içi değil.

Yukarıdaki durumda --preserveConstEnums bayrağının Bread'ın bir arama tablosu yayınlamasına neden olacağını unutmayın. Yine de değerleri satır içi olacaktır.

enum beyan

Diğer kullanımlarında olduğu gibi declare, declarehiçbir kod yaymaz ve gerçek kodu başka bir yerde tanımlamış olmanızı bekler. Bu, arama tablosu yaymaz:

declare enum Wine { Red, Wine }

Wine.RedWine.RedJavaScript'te yayınlar , ancak başvurulacak herhangi bir Wine arama tablosu olmayacağından, başka bir yerde tanımlamadıysanız bu bir hatadır.

sabit enum bildir

Bu, arama tablosu yaymaz:

declare const enum Fruit { Apple, Pear }

Ama satır içi yapıyor! Fruit.Apple0 yayar. Ancak Fruit[0], satır içi olmadığından ve arama tablosu olmadığından çalışma zamanında yine hata verecektir.

Bunu bu oyun alanında yazdım . Hangi TypeScript'in hangi JavaScript'i yayınladığını anlamak için orada oynamanızı öneririm.


1
Bu yanıtı güncellemenizi öneririm: Typescript 3.3.3'ten itibaren, Bread[0]bir derleyici hatası veriyor : "Bir const enum üyesine yalnızca bir dize değişmezi kullanılarak erişilebilir."
chharvey

1
Hm ... cevabın söylediğinden farklı mı? "Peki Bread [0] ne olacak? Bu çalışma zamanında hata verecektir ve derleyiciniz onu yakalamalıdır. Arama tablosu yok ve derleyici burada satır içi değil."
Kat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.