readonly menandai property yang hanya boleh diisi sekali, saat object dibuat, dan tidak pernah di-assign ulang. Readonly<T> menerapkannya ke setiap property sebuah tipe, dan readonly T[] melakukan hal yang sama untuk array:
Baris terakhir menunjukkan fakta terpenting tentang readonly: ia dicek oleh compiler, bukan ditegakkan saat runtime. Assignment itu adalah error kompilasi (diredam di sini dengan @ts-expect-error), tetapi JavaScript yang dihasilkan tetap menjalankannya. Tanpa peredaman itu, file-nya tidak akan lolos kompilasi, dan di situlah readonly menjalankan tugasnya.
Property readonly
Taruh readonly sebelum nama property di interface, type literal, atau class. Property itu boleh diinisialisasi, tetapi tidak boleh di-assign ulang:
Di class, field readonly boleh di-assign di deklarasinya atau di constructor, dan tidak di tempat lain. Bentuk paling singkatnya adalah parameter property, constructor(readonly id: string) {}, yang mendeklarasikan dan meng-assign field dalam satu langkah. Halaman class membahas field dan constructor secara umum.
Readonly<T>: Semua Property Sekaligus
Readonly<T> adalah utility type yang menandai setiap property T sebagai readonly. Tipe ini berguna untuk nilai yang kamu teruskan ke mana-mana tetapi tidak boleh diubah, seperti state aplikasi:
Signature fungsinya memberi tahu pembaca bahwa addItem mengembalikan state baru alih-alih mengubah yang lama, dan compiler memastikan fungsinya menepati janji itu. Readonly<T> didefinisikan sebagai mapped type { readonly [P in keyof T]: T[P] }.
Array Readonly: readonly T[] dan ReadonlyArray<T>
readonly number[] dan ReadonlyArray<number> adalah tipe yang sama. Keduanya menghapus setiap method yang mengubah isi (push, pop, shift, splice, sort, reverse, fill...) dan melarang assignment lewat index. Method yang tidak mengubah isi tetap ada dan mengembalikan array biasa:
Menerima readonly T[] sebagai parameter adalah janji kepada pemanggil bahwa kamu tidak akan mengubah array mereka. Arah sebaliknya yang sering membuat orang bingung: array readonly tidak bisa dikirim ke fungsi yang menerima T[] biasa, karena fungsi itu mungkin mengubah isinya.
index.ts(7,17): error TS4104: The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
Perbaikannya adalah mengubah sum supaya menerima readonly number[], karena fungsi itu tidak mengubah apa pun. Fungsi yang hanya membaca array sebaiknya selalu menerima tipe readonly; dengan begitu ia menerima kedua jenis array. Kalau fungsinya bukan milikmu, kirim salinannya: sum([...prices]).
ReadonlyMap dan ReadonlySet
Map dan set juga punya versi readonly. ReadonlyMap<K, V> punya get, has, size, forEach dan iterator, tetapi tidak punya set, delete atau clear; ReadonlySet<T> tidak punya add, delete atau clear:
Sebuah class sering menyimpan Map private yang mutable dan mengeksposnya lewat getter bertipe ReadonlyMap, sehingga kode di luar bisa membaca datanya tetapi tidak bisa mengubahnya lewat referensi itu.
readonly Bersifat Dangkal
readonly dan Readonly<T> hanya melindungi property itu sendiri, bukan object atau array yang dirujuknya:
DeepReadonly<T> menerapkan dirinya sendiri ke setiap object type yang bersarang, dan karena mapped type atas tipe array menghasilkan array readonly, members menjadi readonly string[]. Ini tetap janji di level tipe, bukan perlindungan saat runtime.
Hanya Saat Kompilasi: Perubahan Lewat Referensi Lain
Tipe readonly mengatur apa yang boleh dilakukan oleh satu referensi. Referensi lain ke object yang sama, yang bertipe tanpa readonly, bisa mengubahnya, dan TypeScript bahkan mengizinkan tipe readonly di-assign ke tipe yang mutable:
Assignment mutable = settings lolos kompilasi karena TypeScript tidak memperhitungkan property readonly saat mengecek apakah dua object type kompatibel; TypeScript handbook menyatakannya secara langsung, dan mencatat bahwa property readonly karena itu bisa berubah lewat aliasing. Array readonly berbeda: error TS4104 di atas adalah pengecekan itu. Object.freeze benar-benar mencegah perubahan saat runtime: kode yang dihasilkan berjalan dalam strict mode, dan di sana menulis ke property yang dibekukan akan melempar TypeError. Seperti readonly, Object.freeze juga dangkal.
readonly, const, as const dan Object.freeze
as const pada literal membuat setiap property menjadi readonly di setiap kedalaman dan mempertahankan literal type, yang sering menjadi cara termudah untuk mendapatkan nilai yang readonly secara mendalam:
const theme = { mode: "dark", sizes: [12, 14] } as const;
// { readonly mode: "dark"; readonly sizes: readonly [12, 14] }
| Berlaku untuk | Deep? | Efek saat runtime | Contoh | |
|---|---|---|---|---|
const | binding variabel | tidak | variabel tidak bisa di-assign ulang | const user = {...} |
readonly | satu property atau tipe array | tidak | tidak ada | readonly id: string |
Readonly<T> | setiap property sebuah tipe | tidak | tidak ada | Readonly<State> |
as const | ekspresi literal | ya | tidak ada | { ... } as const |
Object.freeze | nilai object | tidak | penulisan gagal (melempar error di strict mode) | Object.freeze(obj) |
const dan readonly menjawab pertanyaan yang berbeda: const mencegah nama itu menunjuk ke tempat lain, readonly mencegah sebuah property berubah. Property dari object const tetap bisa di-assign ulang kecuali property itu readonly.
Pertanyaan yang sering diajukan
Apa fungsi readonly di TypeScript?
readonly menandai property yang boleh diisi saat object dibuat (atau di constructor class), tetapi tidak boleh di-assign ulang sesudahnya. Meng-assign-nya belakangan adalah error kompilasi, TS2540. Ini hanya pengecekan tipe: JavaScript yang dihasilkan tidak memuat perlindungan apa pun.
Apa perbedaan readonly dan const di TypeScript?
const berlaku untuk variabel: nama itu tidak bisa diarahkan ke nilai lain, tetapi object yang dipegangnya tetap bisa diubah. readonly berlaku untuk property: property itu tidak bisa di-assign ulang. const user = { name: "Ada" } masih mengizinkan user.name = "x"; property readonly name tidak.
Bagaimana cara membuat array readonly di TypeScript?
Anotasi sebagai readonly T[] atau ReadonlyArray<T> (tipe yang sama). Method yang mengubah isi seperti push, pop, sort dan splice hilang dari tipenya, dan assignment lewat index menjadi error. Method yang tidak mengubah isi seperti map, filter dan slice tetap berfungsi dan mengembalikan array biasa.
Apakah Readonly bersifat dalam (deep) di TypeScript?
Tidak. Readonly<T> dan readonly hanya melindungi property di level teratas; object dan array bersarang di dalamnya tetap bisa diubah. Pakai as const pada literal, atau tulis tipe rekursif DeepReadonly<T>, untuk perlindungan yang dalam di level tipe.
Apakah readonly mencegah perubahan saat runtime?
Tidak. Tipe dihapus saat kompilasi, jadi property readonly adalah property biasa saat runtime, dan kode yang punya referensi mutable ke object yang sama (atau JavaScript biasa) tetap bisa mengubahnya. Pakai Object.freeze kalau kamu butuh perlindungan saat runtime; TypeScript memberi hasilnya tipe Readonly<T>.