Jelajahi alternatif enum TypeScript, termasuk const assertions dan union types, serta pelajari kapan menggunakan masing-masing untuk pemeliharaan kode dan kinerja optimal.
Alternatif Enum TypeScript: Const Assertions vs. Union Types
enum TypeScript adalah fitur canggih untuk mendefinisikan sekumpulan konstanta bernama. Namun, ini tidak selalu merupakan pilihan terbaik. Artikel ini membahas alternatif untuk enum, khususnya const assertions dan union types, serta memberikan panduan kapan harus menggunakan masing-masing untuk kualitas kode, pemeliharaan, dan kinerja yang optimal. Kami akan menggali nuansa dari setiap pendekatan, menawarkan contoh praktis dan menangani masalah umum.
Memahami Enum TypeScript
Sebelum membahas alternatif, mari kita tinjau singkat enum TypeScript. Enum adalah cara untuk mendefinisikan sekumpulan konstanta numerik bernama. Secara default, anggota enum pertama diberi nilai 0, dan anggota berikutnya diinkrementasikan sebesar 1.
enum Status {
Pending,
InProgress,
Completed,
Rejected,
}
const currentStatus: Status = Status.InProgress; // currentStatus will be 1
Anda juga dapat secara eksplisit menetapkan nilai ke anggota enum:
enum HTTPStatus {
OK = 200,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404,
}
const serverResponse: HTTPStatus = HTTPStatus.OK; // serverResponse will be 200
Manfaat Enums
- Keterbacaan: Enum meningkatkan keterbacaan kode dengan menyediakan nama yang berarti untuk konstanta numerik.
- Keamanan Tipe: Mereka memberlakukan keamanan tipe dengan membatasi nilai pada anggota enum yang ditentukan.
- Pelengkapan Otomatis: IDE menyediakan saran pelengkapan otomatis untuk anggota enum, mengurangi kesalahan.
Kekurangan Enums
- Overhead Runtime: Enum dikompilasi menjadi objek JavaScript, yang dapat menimbulkan overhead runtime, terutama dalam aplikasi besar.
- Mutasi: Enum bersifat dapat diubah secara default. Meskipun TypeScript menyediakan
const enumuntuk mencegah mutasi, ia memiliki keterbatasan. - Pemetaan Balik: Enum numerik membuat pemetaan balik (misalnya,
Status[1]mengembalikan "InProgress"), yang seringkali tidak perlu dan dapat meningkatkan ukuran bundel.
Alternatif 1: Const Assertions
Const assertions menyediakan cara untuk membuat struktur data yang tidak dapat diubah (immutable) dan hanya baca (readonly). Mereka dapat digunakan sebagai alternatif untuk enum dalam banyak kasus, terutama saat Anda memerlukan sekumpulan konstanta string atau numerik yang sederhana.
const Status = {
Pending: 'pending',
InProgress: 'in_progress',
Completed: 'completed',
Rejected: 'rejected',
} as const;
// Typescript infers the following type:
// {
// readonly Pending: "pending";
// readonly InProgress: "in_progress";
// readonly Completed: "completed";
// readonly Rejected: "rejected";
// }
type StatusType = typeof Status[keyof typeof Status]; // 'pending' | 'in_progress' | 'completed' | 'rejected'
function processStatus(status: StatusType) {
console.log(`Processing status: ${status}`);
}
processStatus(Status.InProgress); // Valid
// processStatus('invalid'); // Error: Argument of type '"invalid"' is not assignable to parameter of type 'StatusType'.
Dalam contoh ini, kita mendefinisikan objek JavaScript biasa dengan nilai string. Assertion as const memberitahu TypeScript untuk memperlakukan objek ini sebagai hanya baca dan menyimpulkan tipe yang paling spesifik untuk propertinya. Kemudian kita mengekstrak tipe union dari kunci-kunci tersebut. Pendekatan ini menawarkan beberapa keuntungan:
Manfaat Const Assertions
- Immutabilitas: Const assertions membuat struktur data yang tidak dapat diubah, mencegah modifikasi yang tidak disengaja.
- Tanpa Overhead Runtime: Mereka adalah objek JavaScript sederhana, sehingga tidak ada overhead runtime yang terkait dengan enum.
- Keamanan Tipe: Mereka menyediakan keamanan tipe yang kuat dengan membatasi nilai pada konstanta yang ditentukan.
- Ramah Tree-shaking: Bundler modern dapat dengan mudah melakukan tree-shaking nilai yang tidak digunakan, mengurangi ukuran bundel.
Pertimbangan untuk Const Assertions
- Lebih bertele-tele: Mendefinisikan dan menetapkan tipe bisa sedikit lebih bertele-tele daripada enum, terutama untuk kasus sederhana.
- Tidak Ada Pemetaan Balik: Mereka tidak menyediakan pemetaan balik, tetapi ini seringkali merupakan keuntungan daripada kekurangan.
Alternatif 2: Union Types
Union types memungkinkan Anda mendefinisikan variabel yang dapat menampung salah satu dari beberapa tipe yang mungkin. Mereka adalah cara yang lebih langsung untuk mendefinisikan nilai yang diizinkan tanpa objek, yang bermanfaat ketika Anda tidak memerlukan hubungan key-value dari enum atau const assertion.
type Status = 'pending' | 'in_progress' | 'completed' | 'rejected';
function processStatus(status: Status) {
console.log(`Processing status: ${status}`);
}
processStatus('in_progress'); // Valid
// processStatus('invalid'); // Error: Argument of type '"invalid"' is not assignable to parameter of type 'Status'.
Ini adalah cara yang ringkas dan aman tipe untuk mendefinisikan sekumpulan nilai yang diizinkan.
Manfaat Union Types
- Keringkasan: Union types adalah pendekatan yang paling ringkas, terutama untuk sekumpulan konstanta string atau numerik sederhana.
- Keamanan Tipe: Mereka menyediakan keamanan tipe yang kuat dengan membatasi nilai pada opsi yang ditentukan.
- Tanpa Overhead Runtime: Union types hanya ada pada waktu kompilasi dan tidak memiliki representasi runtime.
Pertimbangan untuk Union Types
- Tidak Ada Asosiasi Key-Value: Mereka tidak menyediakan hubungan key-value seperti enum atau const assertions. Ini berarti Anda tidak dapat dengan mudah mencari nilai berdasarkan namanya.
- Pengulangan Literal String: Anda mungkin perlu mengulang literal string jika Anda menggunakan kumpulan nilai yang sama di beberapa tempat. Ini dapat diringankan dengan definisi `type` bersama.
Kapan Menggunakan yang Mana?
Pendekatan terbaik tergantung pada kebutuhan dan prioritas spesifik Anda. Berikut panduan untuk membantu Anda memilih:
- Gunakan Enum Ketika:
- Anda memerlukan sekumpulan konstanta numerik sederhana dengan peningkatan implisit.
- Anda memerlukan pemetaan balik (meskipun ini jarang diperlukan).
- Anda bekerja dengan kode lama yang sudah banyak menggunakan enum dan Anda tidak memiliki kebutuhan mendesak untuk mengubahnya.
- Gunakan Const Assertions Ketika:
- Anda memerlukan sekumpulan konstanta string atau numerik yang harus bersifat immutable.
- Anda memerlukan hubungan key-value dan ingin menghindari overhead runtime.
- Tree-shaking dan ukuran bundel adalah pertimbangan penting.
- Gunakan Union Types Ketika:
- Anda memerlukan cara yang sederhana dan ringkas untuk mendefinisikan sekumpulan nilai yang diizinkan.
- Anda tidak memerlukan hubungan key-value.
- Kinerja dan ukuran bundel sangat penting.
Skenario Contoh: Mendefinisikan Peran Pengguna
Mari kita pertimbangkan skenario di mana Anda perlu mendefinisikan peran pengguna dalam sebuah aplikasi. Anda mungkin memiliki peran seperti "Admin", "Editor", dan "Viewer".
Menggunakan Enum:
enum UserRole {
Admin,
Editor,
Viewer,
}
function authorize(role: UserRole) {
// ...
}
Menggunakan Const Assertions:
const UserRole = {
Admin: 'admin',
Editor: 'editor',
Viewer: 'viewer',
} as const;
type UserRoleType = typeof UserRole[keyof typeof UserRole];
function authorize(role: UserRoleType) {
// ...
}
Menggunakan Union Types:
type UserRole = 'admin' | 'editor' | 'viewer';
function authorize(role: UserRole) {
// ...
}
Dalam skenario ini, union types menawarkan solusi yang paling ringkas dan efisien. Const assertions adalah alternatif yang baik jika Anda lebih memilih hubungan key-value, mungkin untuk mencari deskripsi setiap peran. Enum umumnya tidak direkomendasikan di sini kecuali Anda memiliki kebutuhan spesifik untuk nilai numerik atau pemetaan balik.
Skenario Contoh: Mendefinisikan Kode Status Endpoint API
Mari kita pertimbangkan skenario di mana Anda perlu mendefinisikan kode status endpoint API. Anda mungkin memiliki kode seperti 200 (OK), 400 (Bad Request), 401 (Unauthorized) dan 500 (Internal Server Error).
Menggunakan Enum:
enum StatusCode {
OK = 200,
BadRequest = 400,
Unauthorized = 401,
InternalServerError = 500
}
function processStatus(code: StatusCode) {
// ...
}
Menggunakan Const Assertions:
const StatusCode = {
OK: 200,
BadRequest: 400,
Unauthorized: 401,
InternalServerError: 500
} as const;
type StatusCodeType = typeof StatusCode[keyof typeof StatusCode];
function processStatus(code: StatusCodeType) {
// ...
}
Menggunakan Union Types:
type StatusCode = 200 | 400 | 401 | 500;
function processStatus(code: StatusCode) {
// ...
}
Sekali lagi, union types menawarkan solusi yang paling ringkas dan efisien. Const assertions adalah alternatif yang kuat dan mungkin lebih disukai karena memberikan deskripsi yang lebih verbose untuk kode status tertentu. Enum dapat berguna jika pustaka eksternal atau API mengharapkan kode status berbasis integer, dan Anda ingin memastikan integrasi yang mulus. Nilai numerik selaras dengan kode HTTP standar, berpotensi menyederhanakan interaksi dengan sistem yang ada.
Pertimbangan Kinerja
Dalam kebanyakan kasus, perbedaan kinerja antara enum, const assertions, dan union types dapat diabaikan. Namun, dalam aplikasi yang kritis terhadap kinerja, penting untuk menyadari potensi perbedaannya.
- Enums: Enum memperkenalkan overhead runtime karena pembuatan objek JavaScript. Overhead ini bisa signifikan dalam aplikasi besar dengan banyak enum.
- Const Assertions: Const assertions tidak memiliki overhead runtime. Mereka adalah objek JavaScript sederhana yang diperlakukan sebagai hanya baca oleh TypeScript.
- Union Types: Union types tidak memiliki overhead runtime. Mereka hanya ada pada waktu kompilasi dan dihapus selama kompilasi.
Jika kinerja menjadi perhatian utama, union types umumnya merupakan pilihan terbaik. Const assertions juga merupakan pilihan yang baik, terutama jika Anda memerlukan hubungan key-value. Hindari penggunaan enum di bagian kode yang kritis terhadap kinerja kecuali Anda memiliki alasan khusus untuk melakukannya.
Implikasi Global dan Praktik Terbaik
Saat bekerja pada proyek dengan tim internasional atau pengguna global, sangat penting untuk mempertimbangkan lokalisasi dan internasionalisasi. Berikut adalah beberapa praktik terbaik untuk menggunakan enum dan alternatifnya dalam konteks global:
- Gunakan nama deskriptif: Pilih nama anggota enum (atau kunci const assertion) yang jelas dan tidak ambigu, bahkan untuk penutur non-bahasa Inggris. Hindari bahasa gaul atau jargon.
- Pertimbangkan lokalisasi: Jika Anda perlu menampilkan nama anggota enum kepada pengguna, pertimbangkan untuk menggunakan pustaka lokalisasi untuk menyediakan terjemahan untuk berbagai bahasa. Misalnya, alih-alih langsung menampilkan `Status.InProgress`, Anda mungkin menampilkan `i18n.t('status.in_progress')`.
- Hindari asumsi yang spesifik budaya: Perhatikan perbedaan budaya saat mendefinisikan nilai enum. Misalnya, format tanggal, simbol mata uang, dan unit pengukuran dapat sangat bervariasi di berbagai budaya. Jika Anda perlu merepresentasikan nilai-nilai ini, pertimbangkan untuk menggunakan pustaka yang menangani lokalisasi dan internasionalisasi.
- Dokumentasikan kode Anda: Sediakan dokumentasi yang jelas dan ringkas untuk enum dan alternatifnya, jelaskan tujuan dan penggunaannya. Ini akan membantu pengembang lain memahami kode Anda, terlepas dari latar belakang atau pengalaman mereka.
Contoh: Melokalisasi Peran Pengguna
Mari kita kembali ke contoh peran pengguna dan mempertimbangkan bagaimana melokalisasi nama peran untuk berbagai bahasa.
// Using Const Assertions with Localization
const UserRole = {
Admin: 'admin',
Editor: 'editor',
Viewer: 'viewer',
} as const;
type UserRoleType = typeof UserRole[keyof typeof UserRole];
// Localization function (using a hypothetical i18n library)
function getLocalizedRoleName(role: UserRoleType, locale: string): string {
switch (role) {
case UserRole.Admin:
return i18n.t('user_role.admin', { locale });
case UserRole.Editor:
return i18n.t('user_role.editor', { locale });
case UserRole.Viewer:
return i18n.t('user_role.viewer', { locale });
default:
return 'Unknown Role';
}
}
// Example usage
const currentRole: UserRoleType = UserRole.Editor;
const localizedRoleName = getLocalizedRoleName(currentRole, 'fr-CA'); // Returns localized "Éditeur" for French Canadian.
console.log(`Current role: ${localizedRoleName}`);
Dalam contoh ini, kami menggunakan fungsi lokalisasi untuk mengambil nama peran yang diterjemahkan berdasarkan lokal pengguna. Ini memastikan bahwa nama peran ditampilkan dalam bahasa pilihan pengguna.
Kesimpulan
Enum TypeScript adalah fitur yang berguna, tetapi tidak selalu merupakan pilihan terbaik. Const assertions dan union types menawarkan alternatif yang layak yang dapat memberikan kinerja, imutabilitas, dan pemeliharaan kode yang lebih baik. Dengan memahami manfaat dan kekurangan dari setiap pendekatan, Anda dapat membuat keputusan yang tepat tentang mana yang akan digunakan dalam proyek Anda. Pertimbangkan kebutuhan spesifik aplikasi Anda, preferensi tim Anda, dan pemeliharaan jangka panjang kode Anda. Dengan mempertimbangkan faktor-faktor ini secara cermat, Anda dapat memilih pendekatan terbaik untuk mendefinisikan konstanta dalam proyek TypeScript Anda, menghasilkan basis kode yang lebih bersih, lebih efisien, dan lebih mudah dipelihara.