Hampir setiap program yang kamu tulis pasti butuh menyimpan sekumpulan data: daftar nama pengguna, daftar harga, daftar titik pada sebuah grafik. Di JavaScript biasa, kamu tinggal memasukkan nilai ke dalam array lalu berharap masih ingat isinya apa saja. TypeScript menambahkan satu pertanyaan kecil yang ternyata menyelamatkan kamu dari banyak bug: daftar ini isinya nilai jenis apa, dan jumlahnya berapa?
Dari pertanyaan itu lahir dua alat. Array adalah daftar yang bisa membesar dan mengecil, di mana setiap isinya bertipe sama. Tuple adalah wadah berukuran tetap, di mana tiap posisi punya makna dan tipenya sendiri. Di kode keduanya terlihat mirip, padahal masalah yang mereka selesaikan berbeda jauh. Setelah membaca artikel ini, kamu akan tahu persis kapan harus pakai yang mana, sekaligus cara mengunci keduanya agar tidak ada yang mengubah data yang seharusnya tidak boleh diutak-atik.
Memberi tipe pada array sederhana
Mari mulai dari kasus paling umum: daftar yang seluruh isinya bertipe sama. Di TypeScript, ini ditulis dengan menyebut tipe isinya lalu diikuti tanda kurung siku.
const names: string[] = ["Jane Doe", "John Doe"];
const scores: number[] = [98, 72, 85];
const flags: boolean[] = [true, false, true];
Baca string[] sebagai “array berisi string”. Tanda kurung siku itu artinya “banyak nilai seperti ini”. Begitu kamu menyatakan names bertipe string[], TypeScript otomatis menjaganya. Coba masukkan nilai dengan tipe yang salah, dan ia akan menghentikanmu sebelum kode sempat berjalan:
names.push("Sam Doe"); // aman, sebuah string
names.push(42); // Error: number tidak cocok dengan string
Justru di situlah letak manfaatnya. Di JavaScript biasa, 42 tadi masuk diam-diam dan baru meledak belakangan, mungkin saat sudah di produksi, mungkin di dalam fungsi yang berharap menerima teks. TypeScript menangkapnya tepat saat kamu mengetiknya.
Panjang array tidak tetap. Kamu bisa mulai dari kosong lalu menambah isi, atau menghapusnya, dan tipenya tetap sama.
const queue: string[] = [];
queue.push("first");
queue.push("second");
queue.pop();
Biarkan TypeScript menebak kalau bisa
Kalau kamu menulis const scores = [98, 72, 85], TypeScript sudah bisa menyimpulkan sendiri bahwa tipenya number[]. Anotasi tipe baru perlu kamu tulis secara eksplisit saat array dimulai dari kosong, atau ketika kamu ingin aturan yang lebih ketat dari tebakan TypeScript.
Cara penulisan lain: Array<number>
Di proyek nyata kamu akan menemui gaya penulisan kedua. Alih-alih number[], sebagian orang menulis Array<number>. Keduanya persis tipe yang sama — hanya dua cara menulis ide yang sama.
const a: number[] = [1, 2, 3];
const b: Array<number> = [1, 2, 3];
// tipe a dan b identik
Bentuk dengan kurung sudut ini disebut sintaks generik. Bayangkan Array<...> sebagai cetakan, dan isi di dalam kurung yang mengisi bagian kosongnya: “array berisi apa?” — “array berisi number”.
Jadi sebaiknya pakai yang mana? Sebagian besar soal keterbacaan dan kesepakatan tim. Bentuk singkat number[] lebih ringkas dan enak dibaca untuk tipe sederhana. Bentuk Array<...> bisa lebih jelas saat tipe di dalamnya panjang atau berbelit, karena kurungnya mengelompokkan semuanya dengan gamblang.
// versi singkatnya jadi ramai
let pairs: [string, number][];
let pairsAlt: Array<[string, number]>; // bisa dibilang lebih mudah dibaca
Pilih satu gaya dan konsisten di seluruh proyek. Tidak ada yang “lebih benar”.
Tuple: saat posisi punya makna
Sekarang alat yang lebih menarik. Kadang sebuah daftar sebenarnya bukan “banyak hal yang sejenis” — melainkan sekumpulan kecil nilai dengan jumlah tetap, di mana tiap slot punya arti yang spesifik.
Bayangkan koordinat di sebuah peta: selalu tepat dua angka, yang pertama posisi x dan yang kedua posisi y. Keduanya sama-sama angka, tapi tidak bisa ditukar. Itulah tuple: jumlah isi yang tetap, di mana tiap posisi punya tipe dan perannya sendiri.
Tuple ditulis dengan kurung siku juga, tapi tiap posisi disebutkan tipenya satu per satu:
let point: [number, number] = [10, 20];
let user: [string, number] = ["Jane Doe", 29]; // nama, lalu umur
Perhatikan bedanya dengan array. number[] berarti “berapa pun jumlah angka”. [number, number] berarti “tepat dua angka, tidak lebih, tidak kurang”. TypeScript menjaga panjang sekaligus tipe di tiap posisi:
let point: [number, number] = [10, 20];
point = [10, 20, 30]; // Error: kelebihan elemen
point = ["10", 20]; // Error: posisi 0 harus number
Tuple boleh mencampur tipe dengan bebas, dan justru di situlah ia unggul dibanding array biasa:
// [sedang login, username, jumlah login]
let session: [boolean, string, number] = [true, "John Doe", 3];
Mengambil nilai dari tuple
Mengambil nilai dari tuple caranya sama seperti array — lewat indeks, atau dengan destructuring. Karena TypeScript tahu tipe tiap posisi, ia otomatis memberikan tipe yang tepat untuk masing-masing.
let user: [string, number] = ["Jane Doe", 29];
const name = user[0]; // bertipe string
const age = user[1]; // bertipe number
// atau, lebih enak dibaca, dengan destructuring:
const [fullName, years] = user;
Ini jauh lebih rapi dibanding array (string | number)[] biasa, di mana tiap isinya bisa salah satu dari dua tipe dan TypeScript tidak bisa membedakan yang mana.
Kapan tuple jadi pilihan tepat?
Tuple cocok dipakai saat kamu punya sekelompok kecil nilai dengan jumlah tetap yang saling berkaitan, dan urutannya membawa makna. Beberapa contoh nyata sehari-hari:
- Koordinat 2D:
[number, number]. - Warna RGB:
[number, number, number]. - Pasangan “hasil atau error” yang dikembalikan sebuah fungsi:
[Error | null, Data | null]. - Pasangan kunci/nilai:
[string, number].
Tempat klasik kamu sebenarnya sudah bertemu tuple — sering tanpa sadar — adalah useState di React, yang mengembalikan tepat dua hal dengan urutan tetap: nilai saat ini, dan fungsi untuk memperbaruinya.
// useState mengembalikan tuple: [nilai, setter]
const [count, setCount] = useState(0);
Walau begitu, tuple punya satu batasan nyata: posisinya dikenali lewat urutan, bukan lewat nama. Kalau pembaca tidak tahu bahwa indeks 0 adalah nama dan indeks 1 adalah umur, data itu jadi tebak-tebakan. Maka aturan praktisnya begini:
Pakai object kalau bidangnya lebih dari dua atau tiga
Tuple seperti [string, number, boolean, string] susah dibaca karena makna tiap slotnya tidak kelihatan. Begitu bidangnya jadi banyak, object dengan kunci bernama ({ name, age, isActive }) hampir selalu lebih jelas dan lebih mudah dirawat. Tuple paling pas dijaga tetap pendek.
Berikut data yang sama, ditulis dua cara, supaya kamu bisa merasakan bedanya:
// tuple — ringkas, tapi kamu harus ingat urutannya
const userTuple: [string, number, boolean] = ["Jane Doe", 29, true];
// object — lebih panjang, tapi menjelaskan dirinya sendiri
const userObject = {
name: "Jane Doe",
age: 29,
isActive: true,
};
readonly: mengunci daftar agar tidak berubah
Baik array maupun tuple bisa dijadikan hanya-baca (read-only). Koleksi hanya-baca tetap bisa dibaca dan diulang dengan bebas, tapi tidak bisa diubah — tidak bisa ditambah, dikurangi, atau diganti isinya. Setiap usaha mengubahnya langsung jadi error di mata TypeScript.
Ada dua cara menulisnya. Untuk array, taruh kata kunci readonly di depan:
const colors: readonly string[] = ["red", "green", "blue"];
colors.push("yellow"); // Error: push tidak ada di array readonly
colors[0] = "black"; // Error: indeks readonly tidak bisa diisi ulang
Untuk tuple, pakai kata kunci readonly yang sama:
const origin: readonly [number, number] = [0, 0];
origin[0] = 5; // Error: tuple readonly tidak bisa diubah
Buat apa repot begini? Karena perubahan diam-diam (mutasi) termasuk sumber bug yang paling licik. Saat kamu mengoper array ke sebuah fungsi, fungsi itu bisa saja mengubahnya tanpa kamu sadari, dan data asli ikut berubah di belakang layar. Menandai parameter dengan readonly adalah sebuah janji — kepada dirimu dan rekan timmu — bahwa fungsi itu hanya akan melihat datanya, tidak pernah mengubahnya.
// fungsi ini mustahil mengubah array milik pemanggilnya secara tidak sengaja
function total(values: readonly number[]): number {
let sum = 0;
for (const v of values) sum += v;
return sum;
}
readonly adalah jaminan saat pengecekan, bukan saat berjalan
Perlindungan readonly hanya berlaku selama TypeScript memeriksa kodemu. Saat program berjalan, nilainya tetap array JavaScript biasa, jadi sama sekali tidak menambah beban performa. Ini pagar pengaman untuk orang yang menulis kode, bukan gembok yang dipaksakan oleh mesin JavaScript. Justru karena itulah ia bebas dipakai sebanyak-banyaknya.
Array vs tuple sekilas
Berikut gambaran utuhnya berdampingan, supaya lain kali kamu bisa cepat memutuskan:
| Pertanyaan | Array (number[]) |
Tuple ([number, string]) |
|---|---|---|
| Jumlah isi? | Bebas, bisa bertambah-berkurang | Tetap, ditentukan di awal |
| Tipe isi | Semua tipenya sama | Tiap posisi tipenya sendiri |
| Makna posisi? | Tidak ada yang khusus | Tiap slot punya peran tetap |
| Cocok untuk | Kumpulan hal sejenis | Kelompok kecil tetap, pasangan berurutan |
| Versi hanya-baca | readonly number[] |
readonly [number, string] |
Cara cepat menebaknya: kalau kamu nyaman mengulang semua isinya dengan kode yang sama, berarti yang kamu butuh array. Kalau tiap slot maknanya berbeda dan jumlahnya tidak pernah berubah, berarti yang kamu butuh tuple.
"daftar pengguna" -> array User[]
"koordinat di layar" -> tuple [number, number]
"semua total pesanan" -> array number[]
"nilai-beserta-setternya" -> tuple [State, Setter]
Rangkuman
Array dan tuple adalah dua cara TypeScript menggambarkan sebuah daftar, dan paham kapan memakai masing-masing akan langsung mempertajam tipe yang kamu tulis. Ini yang perlu kamu bawa pulang:
- Array (
string[]atauArray<string>— sama saja) adalah daftar lentur di mana semua isinya bertipe sama dan panjangnya bisa berubah. Pakai untuk kumpulan hal sejenis. - Tuple (
[string, number]) punya panjang tetap di mana tiap posisi punya tipe dan maknanya sendiri. Pakai untuk kelompok kecil yang berurutan, seperti koordinat atau pasangan nilai/setter. - Jaga tuple tetap pendek. Begitu lebih dari dua atau tiga bidang, object dengan kunci bernama lebih jelas.
- Tambahkan
readonlypada salah satunya kalau daftar itu tidak boleh berubah setelah dibuat. Tidak menambah beban saat berjalan, dan mencegah satu kelas bug perubahan diam-diam.
Mulailah dengan array biasa — itu sudah menutupi sebagian besar daftar yang akan kamu tulis. Simpan tuple untuk momen ketika urutan itu sendiri membawa makna, dan tambahkan readonly setiap kali kamu ingin janji tegas bahwa datanya tidak akan bergeser. Tiga kebiasaan itu saja sudah membuat tipemu lebih aman sekaligus lebih enak dibaca.