Enum TypeScript adalah kumpulan konstanta bernama. enum Direction { Up, Down, Left, Right } membuat sebuah tipe, Direction, sekaligus objek runtime yang member-nya kamu akses sebagai Direction.Up. Member diberi nomor mulai dari 0 kecuali kamu memberinya nilai, dan enum string memberi setiap member sebuah string yang mudah dibaca.
Enum adalah salah satu dari sedikit fitur TypeScript yang bukan sekadar tipe: enum menjadi objek JavaScript sungguhan saat kodenya dikompilasi.
Enum Numerik
Tanpa nilai awal, member mendapat 0, 1, 2, dan seterusnya. Beri member pertama sebuah angka, dan sisanya berlanjut dari angka itu. Kamu juga bisa mengatur setiap nilai secara eksplisit, yang merupakan pilihan aman kalau angkanya disimpan di database atau dikirim lewat jaringan.
Mengandalkan penomoran otomatis tidak masalah untuk nilai yang tidak pernah keluar dari program. Kalau urutan member mungkin berubah dan angkanya disimpan di mana pun, menyisipkan member di tengah akan diam-diam menomori ulang semua member setelahnya.
Hasil Kompilasi Sebuah Enum
Tipe dihapus, tetapi enum tidak. Berikut JavaScript yang dihasilkan TypeScript untuk enum numerik dan enum string:
enum Direction { Up, Down, Left, Right }
enum Status { Active = "ACTIVE", Inactive = "INACTIVE" }
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
var Status;
(function (Status) {
Status["Active"] = "ACTIVE";
Status["Inactive"] = "INACTIVE";
})(Status || (Status = {}));
Direction["Up"] = 0 mengembalikan 0, jadi Direction[0] = "Up" diatur dalam pernyataan yang sama. Karena itu, enum numerik memetakan dua arah: dari nama ke angka dan dari angka kembali ke nama. Itulah reverse mapping. Enum string hanya memetakan nama ke nilai.
Objek Direction yang tercetak punya delapan key: empat nama dan empat angka. Hal ini jadi penting begitu kamu mengiterasinya.
Enum String
Setiap member enum string butuh nilai string yang eksplisit. Nilai-nilai itu muncul apa adanya di log, JSON, dan database, yang membuat enum string lebih mudah di-debug daripada angka.
Enum string bersifat nominal dalam satu hal yang sering mengejutkan: string biasa tidak bisa diberikan kepadanya, bahkan ketika teksnya sama dengan nilai sebuah member.
index.ts(7,5): error TS2820: Type '"ACTIVE"' is not assignable to type 'Status'. Did you mean 'Status.Inactive'?
(Saran di pesan itu hanyalah tebakan compiler dan salah di sini; perbaikannya adalah Status.Active.) Ke arah sebaliknya, nilai Status bisa dipakai di mana pun string diharapkan. Kalau nilai datang sebagai string, dari JSON atau form, konversikan dengan pemeriksaan seperti di bagian tentang memeriksa nilai di bawah.
Memakai Enum sebagai Tipe
Nama enum adalah tipe yang nilainya adalah member-member enum itu. Dikombinasikan dengan switch, TypeScript memeriksa bahwa setiap member ditangani ketika fungsi harus mengembalikan nilai:
Kalau member baru ditambahkan ke Shape tanpa case baru, sides tidak lagi bisa dikompilasi dengan TS2366, Function lacks ending return statement and return type does not include 'undefined'. Halaman switch memperlihatkan pemeriksaan menyeluruh berbasis never yang lebih ketat.
Baris-baris terakhir memperlihatkan kelemahan nyata enum numerik. Literal angka yang tidak cocok dengan member mana pun, const level: Level = 99, adalah error kompilasi (TS2322), tetapi nilai apa pun yang bertipe number diterima, jadi 57 bisa lolos. Enum string tidak punya celah ini.
Mengiterasi Enum
Enum adalah objek saat runtime, jadi Object.keys, Object.values, dan Object.entries bisa dipakai. Untuk enum string, ketiganya mengembalikan member persis apa adanya. Untuk enum numerik, ketiganya juga mengembalikan entri reverse mapping, yang perlu kamu saring:
Untuk memberi tipe variabel sebagai "salah satu nama member enum", pakai keyof typeof Direction, yaitu union "Up" | "Down" | "Left" | "Right". Lalu Direction[name] mencari nilainya dengan keamanan tipe penuh.
Enum string tidak punya reverse mapping, jadi untuk mendapatkan nama member dari nilainya, cari di entrinya: Object.entries(Status).find(([, v]) => v === "ACTIVE")?.[0] menghasilkan "Active", atau undefined kalau tidak ada member dengan nilai itu.
Memeriksa Apakah Sebuah Nilai Ada di Enum
Data dari luar program berupa string atau number biasa. Type guard memeriksanya terhadap nilai-nilai enum dan mempersempitnya menjadi tipe enum:
Hindari raw as Status pada input yang tidak tepercaya: assertion itu lolos kompilasi, tetapi tidak ada yang diperiksa saat runtime, jadi "DELETED" akan beredar di program dengan tipe Status yang seolah valid.
const Enum
const enum meminta compiler menghapus enum dan menuliskan nilai setiap member di tempat ia dipakai. Tidak ada objek saat runtime, jadi tidak ada yang bisa diiterasi atau di-reverse mapping.
const enum menghemat beberapa byte dan satu pencarian property, tetapi bergantung pada compiler yang melihat deklarasi enum saat mengompilasi setiap file yang memakainya. Alat yang mentranspilasi satu file sekaligus, seperti Babel dan swc, tidak bisa melihat const enum yang dideklarasikan di file lain; type stripping milik Node menolak const enum seperti enum lainnya; dan dengan isolatedModules atau verbatimModuleSyntax, TypeScript melaporkan error TS2748 saat kamu memakai const enum dari file deklarasi. Sebagian besar kode aplikasi tidak butuh const enum.
Enum vs Union Type vs Objek as const
Ada tiga cara umum untuk mendefinisikan kumpulan nilai yang tetap:
enum | Union berisi literal | Objek as const | |
|---|---|---|---|
| Ada saat runtime | ya, sebuah objek | tidak | ya, objek biasa |
| Mengiterasi nilai | Object.values (numerik: disaring) | tidak, tidak ada yang bisa diiterasi | Object.values |
Menerima "red" biasa | tidak (enum string) | ya | ya |
Akses bernama X.Red | ya | tidak | ya |
| Reverse mapping | hanya enum numerik | tidak | tidak |
| Berjalan dengan type stripping Node | tidak | ya | ya |
Diizinkan oleh erasableSyntaxOnly | tidak | ya | ya |
| Sintaks tambahan yang perlu dipelajari | aturan enum, const enum | tidak ada | pola typeof |
Banyak tim sekarang memakai union berisi string literal secara default, lalu beralih ke objek as const kalau mereka butuh nilainya saat runtime (untuk diiterasi atau membuat dropdown). Alasannya: union adalah tipe murni dan hilang dari output; union menerima string biasa yang dikirim JSON dan API; dan enum adalah satu-satunya bagian TypeScript sehari-hari yang bukan "JavaScript ditambah tipe yang bisa dihapus".
Poin terakhir itu kini terasa dalam praktik. Node menjalankan file .ts secara langsung dengan membuang tipenya, dan enum bukan sesuatu yang bisa dibuang:
node status.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode
Flag --experimental-transform-types milik Node membuat enum bisa berjalan, dan opsi compiler erasableSyntaxOnly melaporkan setiap enum sebagai error TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., sehingga sebuah proyek bisa melarangnya sejak awal. Lihat menjalankan TypeScript untuk cara kerja type stripping. Semua ini tidak membuat enum salah: kode yang dikompilasi dengan tsc atau bundler menjalankannya dengan baik, dan codebase yang sudah memakai enum tidak banyak diuntungkan dengan mengonversinya.
Pertanyaan yang sering diajukan
Apa itu enum di TypeScript?
Kumpulan konstanta bernama yang sekaligus merupakan tipe dan objek runtime: enum Direction { Up, Down } membuatmu bisa menulis Direction.Up dan memakai Direction sebagai tipe parameter. Tidak seperti sebagian besar fitur TypeScript, enum tidak dihapus: ia dikompilasi menjadi objek JavaScript yang ada saat runtime.
Bagaimana cara mengiterasi enum di TypeScript?
Untuk enum string, Object.values(MyEnum) memberikan nilai-nilainya dan Object.keys(MyEnum) nama-namanya. Enum numerik juga berisi entri reverse mapping ("0": "Up"), jadi saring entri itu: Object.keys(Direction).filter((k) => isNaN(Number(k))) hanya memberikan nama-namanya. const enum tidak bisa diiterasi, karena tidak ada saat runtime.
Bagaimana cara mengubah string menjadi nilai enum di TypeScript?
Periksa string itu terhadap nilai-nilai enum di dalam type guard: function isStatus(s: string): s is Status { return (Object.values(Status) as string[]).includes(s); }. Setelah pemeriksaan, s bertipe Status. s as Status saja memang lolos kompilasi, tetapi tidak memeriksa apa pun saat runtime.
Sebaiknya aku memakai enum atau union type di TypeScript?
Banyak tim lebih memilih union berisi string literal (type Status = "active" | "inactive"), atau objek as const kalau mereka juga butuh nilainya saat runtime. Union dihapus sepenuhnya, bisa dipakai dengan type stripping bawaan Node dan opsi erasableSyntaxOnly, serta menerima string biasa seperti "active". Enum juga tidak masalah, terutama di codebase yang sudah memakainya.
Apa bedanya enum dan const enum?
Enum biasa dikompilasi menjadi objek yang bisa diiterasi dan dicari isinya saat runtime. const enum dihapus saat kompilasi dan setiap pemakaiannya diganti dengan nilainya (Size.Large menjadi 2), jadi tidak ada biaya saat runtime, tetapi tidak bisa diiterasi, dan alat yang mengompilasi satu file sekaligus membatasi pemakaiannya.