Ada empat aspek berbeda untuk enum di TypeScript yang perlu Anda waspadai. Pertama, beberapa definisi:
"objek pencarian"
Jika Anda menulis enum ini:
enum Foo { X, Y }
TypeScript akan memancarkan objek berikut:
var Foo;
(function (Foo) {
Foo[Foo["X"] = 0] = "X";
Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));
Saya akan merujuk ini sebagai objek pencarian . Tujuannya ada dua: berfungsi sebagai pemetaan dari string ke angka , misalnya saat menulis Foo.Xatau Foo['X'], dan berfungsi sebagai pemetaan dari angka ke string . Pemetaan terbalik itu berguna untuk tujuan debugging atau logging - Anda akan sering memiliki nilai 0atau 1dan ingin mendapatkan string yang sesuai "X"atau "Y".
"menyatakan" atau " ambient "
Dalam TypeScript, Anda dapat "mendeklarasikan" hal-hal yang harus diketahui oleh compiler, tetapi tidak benar-benar mengeluarkan kode. Ini berguna ketika Anda memiliki pustaka seperti jQuery yang mendefinisikan beberapa objek (misalnya $) yang Anda inginkan informasinya diketik, tetapi tidak memerlukan kode apa pun yang dibuat oleh kompiler. Spesifikasi dan dokumentasi lainnya mengacu pada deklarasi yang dibuat dengan cara ini sebagai konteks "ambient"; penting untuk dicatat bahwa semua deklarasi dalam .d.tsfile bersifat "ambient" (baik membutuhkan declarepengubah eksplisit atau memilikinya secara implisit, tergantung pada jenis deklarasi).
"inlining"
Untuk alasan performa dan ukuran kode, sering kali lebih baik jika referensi ke anggota enum diganti dengan padanan numeriknya saat dikompilasi:
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
Spek menyebut substitusi ini , saya akan menyebutnya sebaris karena terdengar lebih keren. Terkadang Anda tidak ingin anggota enum menjadi sebaris, misalnya karena nilai enum mungkin berubah di versi API yang akan datang.
Enums, bagaimana cara kerjanya?
Mari kita uraikan ini dengan setiap aspek enum. Sayangnya, masing-masing dari empat bagian ini akan mereferensikan istilah-istilah dari yang lainnya, jadi Anda mungkin perlu membaca keseluruhan ini lebih dari sekali.
dihitung vs tidak dihitung (konstan)
Anggota enum dapat dihitung atau tidak. Spec memanggil anggota yang tidak dihitung konstan , tetapi saya akan menyebutnya non-dihitung untuk menghindari kebingungan dengan const .
Sebuah dihitung anggota enum adalah salah satu yang nilainya tidak diketahui pada saat kompilasi. Referensi ke anggota yang dihitung tidak dapat disisipkan, tentu saja. Sebaliknya, non-computed anggota enum sekali yang nilainya adalah dikenal pada saat kompilasi. Referensi ke anggota yang tidak dihitung selalu sebaris.
Anggota enum mana yang dihitung dan mana yang tidak dihitung? Pertama, semua anggota constenum adalah konstan (yaitu tidak dihitung), seperti namanya. Untuk enum non-konst, itu tergantung pada apakah Anda melihat enum ambien (deklarasikan) atau enum non-ambien.
Anggota a declare enum(yaitu enum ambien) konstan jika dan hanya jika memiliki penginisialisasi. Jika tidak, itu dihitung. Perhatikan bahwa dalam a declare enum, hanya penginisialisasi numerik yang diperbolehkan. Contoh:
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
Akhirnya, anggota non-deklarasikan non-const enum selalu dianggap dihitung. Namun, ekspresi inisialisasi mereka dikurangi menjadi konstanta jika dapat dihitung pada waktu kompilasi. Artinya, anggota enum non-konst tidak pernah sebaris (perilaku ini diubah di TypeScript 1.5, lihat "Perubahan di TypeScript" di bagian bawah)
const vs non-const
const
Deklarasi enum dapat memiliki constpengubah. Jika enum adalah const, semua referensi ke anggotanya disisipkan.
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
const enums tidak menghasilkan objek pencarian saat dikompilasi. Karena alasan ini, adalah kesalahan untuk merujuk Foopada kode di atas kecuali sebagai bagian dari referensi anggota. Tidak ada Fooobjek yang akan hadir saat runtime.
non-const
Jika deklarasi enum tidak memiliki constpengubah, referensi ke anggotanya hanya sebaris jika anggota tersebut tidak dihitung. Enum non-const, non-deklarasikan akan menghasilkan objek pencarian.
menyatakan (ambient) vs non-deklarasikan
Kata pengantar yang penting adalah bahwa declaredi TypeScript memiliki arti yang sangat spesifik: Objek ini ada di tempat lain . Ini untuk mendeskripsikan objek yang ada . Menggunakan declareuntuk mendefinisikan objek yang sebenarnya tidak ada dapat memiliki konsekuensi yang buruk; kita akan menjelajahinya nanti.
menyatakan
A declare enumtidak akan memancarkan objek pencarian. Referensi ke anggotanya menjadi inline jika anggota tersebut dihitung (lihat di atas di computed vs non-computed).
Sangat penting untuk dicatat bahwa bentuk-bentuk lain dari referensi untuk declare enum yang diperbolehkan, misalnya kode ini tidak error kompilasi tapi akan gagal saat runtime:
// 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
Kesalahan ini termasuk dalam kategori "Jangan berbohong pada kompiler". Jika Anda tidak memiliki objek bernama Foosaat runtime, jangan menulis declare enum Foo!
A declare const enumtidak berbeda dari a const enum, kecuali dalam kasus --preserveConstEnums (lihat di bawah).
non-deklarasikan
Enum yang tidak mendeklarasikan menghasilkan objek pencarian jika tidak const. Sebaris dijelaskan di atas.
--preserveConstEnums
Bendera ini memiliki satu efek: non-deklarasikan enum const akan memancarkan objek pencarian. Sebaris tidak terpengaruh. Ini berguna untuk debugging.
Kesalahan Umum
Kesalahan paling umum adalah menggunakan a declare enumwhen a regular enumatau const enumakan lebih tepat. Bentuk umum adalah ini:
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]);
}
Ingat aturan emas: Jangan pernah declarehal-hal yang sebenarnya tidak ada . Gunakan const enumjika Anda selalu ingin sebaris, atau enumjika Anda menginginkan objek pencarian.
Perubahan TypeScript
Antara TypeScript 1.4 dan 1.5, ada perubahan dalam perilaku (lihat https://github.com/Microsoft/TypeScript/issues/2183 ) untuk membuat semua anggota non-deklarasikan enum non-konst diperlakukan sebagai dihitung, bahkan jika mereka secara eksplisit diinisialisasi dengan literal. Ini "memisahkan bayi", sehingga membuat perilaku inlining lebih dapat diprediksi dan lebih jelas memisahkan konsep const enumdari biasa enum. Sebelum perubahan ini, anggota non-konstanta enum yang tidak dihitung dimasukkan lebih agresif.