Masukkan Password
64 Generik Parsial & Hanya Baca
Lecture ini membahas dua built-in generic TypeScript yang sangat sering dipakai, yaitu:
Partial<T>Readonly<T>
Keduanya sebenarnya sederhana kalau kita pahami tujuan masing-masing:
Partial<T>
↓
membuat property menjadi optional
Readonly<T>
↓
membuat value/property tidak bisa diubah
Mari kita bahas dari dasar sampai hubungan dengan Generic yang sudah kita pelajari.
1. Apa itu Built-in Generic?
Sebelumnya kita sudah membuat Generic sendiri:
function identity<T>(value: T): T {
return value;
}
atau:
class ShoppingCart<T> {
// ...
}
Nah, TypeScript juga sudah menyediakan beberapa Generic bawaan yang siap digunakan.
Contohnya:
Partial<T>
Readonly<T>
Jadi kita tidak perlu membuatnya sendiri.
Kita cukup memberikan tipe ke dalam <>.
Misalnya:
Partial<UserSettings>
atau:
Readonly<UserSettings>
2. Partial<T>
Mari mulai dengan Partial.
Masalah yang ingin diselesaikan
Misalnya kita memiliki:
interface UserSettings {
username: string;
email: string;
darkMode: boolean;
language: string;
}
Kalau kita membuat object:
const user: UserSettings = {
username: "John Smith",
email: "john@gmail.com",
darkMode: false,
language: "EN"
};
Semua property wajib ada.
Karena interface mengatakan:
username: string;
email: string;
darkMode: boolean;
language: string;
Tidak ada tanda ?.
Jadi ini error:
const user: UserSettings = {
darkMode: true,
language: "FR"
};
Karena:
username ❌
email ❌
darkMode ✅
language ✅
username dan email wajib ada.
3. Tapi bagaimana kalau kita hanya ingin update sebagian data?
Misalnya user sudah punya:
const user: UserSettings = {
username: "John Smith",
email: "john@gmail.com",
darkMode: false,
language: "EN"
};
Kemudian user ingin mengubah:
darkMode → true
language → FR
Kita tidak ingin mengubah:
username
email
Maka kita mungkin membuat function:
function updateUserSettings(settings: UserSettings) {
// update settings
}
Masalahnya ketika dipanggil:
updateUserSettings({
darkMode: true,
language: "FR"
});
❌ Error.
Karena parameter tersebut bertipe:
UserSettings
dan UserSettings membutuhkan semua property.
4. Solusinya: Partial<UserSettings>
Kita ubah:
function updateUserSettings(
settings: UserSettings
) {
// ...
}
menjadi:
function updateUserSettings(
settings: Partial<UserSettings>
) {
// ...
}
Nah, sekarang berbeda.
Partial<UserSettings> berarti:
Ambil semua property dari
UserSettings, tetapi jadikan semuanya optional.
Jadi TypeScript secara konsep mengubah:
interface UserSettings {
username: string;
email: string;
darkMode: boolean;
language: string;
}
menjadi:
{
username?: string;
email?: string;
darkMode?: boolean;
language?: string;
}
Perhatikan semuanya menjadi ?.
5. Sekarang kita bisa mengirim sebagian property
updateUserSettings({
darkMode: true,
language: "FR"
});
✅ Valid.
Bahkan ini juga valid:
updateUserSettings({
darkMode: true
});
Ini juga:
updateUserSettings({
language: "DE"
});
Bahkan:
updateUserSettings({});
juga valid.
Karena semua property sekarang optional.
6. Partial<T> tidak mengubah tipe property
Ini juga sangat penting.
Misalnya:
interface UserSettings {
username: string;
email: string;
darkMode: boolean;
language: string;
}
Setelah menggunakan:
Partial<UserSettings>
bukan berarti semua menjadi:
any
Bukan.
Tipe masing-masing property tetap sama.
Hanya keberadaannya yang menjadi optional.
Jadi:
username → string | undefined
email → string | undefined
darkMode → boolean | undefined
language → string | undefined
Secara konsep:
{
username?: string;
email?: string;
darkMode?: boolean;
language?: string;
}
Jadi:
darkMode: true
✅
Tetapi:
darkMode: "hello"
❌
Karena darkMode tetap harus boolean kalau diberikan.
7. Analogi Partial
Bayangkan kamu punya formulir:
User Settings Form
Username: John
Email: john@gmail.com
Dark Mode: false
Language: EN
Untuk membuat user baru, mungkin semua field wajib diisi.
Username ✅
Email ✅
Dark Mode ✅
Language ✅
Tapi untuk update, user mungkin hanya ingin mengubah:
Dark Mode → true
Kita tidak perlu mengirim ulang:
Username
Email
Language
Jadi:
UserSettings
↓
SEMUA WAJIB
Partial<UserSettings>
↓
SEMUA BOLEH TIDAK ADA
Itulah kegunaan Partial.
8. Use case Partial yang sangat umum
Update API
Misalnya kita punya:
interface User {
name: string;
age: number;
email: string;
}
Function update:
function updateUser(
id: number,
data: Partial<User>
) {
// update user
}
Kita bisa:
updateUser(1, {
name: "John"
});
atau:
updateUser(1, {
age: 30
});
atau:
updateUser(1, {
name: "John",
age: 30
});
Tanpa harus memberikan:
name
age
email
sekaligus.
9. Partial cocok untuk PATCH/update
Ini salah satu pola yang perlu kamu ingat.
Misalnya:
function updateProduct(
id: number,
changes: Partial<Product>
) {
// ...
}
Artinya:
“Saya menerima perubahan sebagian dari Product.”
Misalnya hanya:
{
price: 100
}
atau:
{
name: "New Product",
price: 100
}
10. Sekarang masuk ke Readonly<T>
Kalau Partial berhubungan dengan:
“Property boleh tidak ada.”
Readonly berhubungan dengan:
“Property tidak boleh diubah setelah dibuat.”
11. Contoh array biasa
Misalnya:
const arr: string[] = ["John", "Mark"];
Kita masih bisa mengubah array:
arr.push("Mary");
Hasilnya:
["John", "Mark", "Mary"]
Kita juga bisa:
arr.pop();
atau:
arr[0] = "Alice";
Jadi array tersebut mutable.
12. Menggunakan Readonly
Kita bisa menulis:
const arr: Readonly<string[]> = [
"John",
"Mark"
];
Sekarang array tersebut read-only.
Jadi:
arr.push("Mary");
❌ Error.
arr.pop();
❌ Error.
arr[0] = "Alice";
❌ Error.
Karena kita sudah mengatakan:
Readonly<string[]>
13. Analogi Readonly
Bayangkan kamu punya dokumen:
┌─────────────────────┐
│ User Data │
│ │
│ Name: John │
│ Age: 30 │
└─────────────────────┘
Kalau dokumen biasa:
BISA DIEDIT ✏️
Kalau Readonly:
READ ONLY 👀
Kamu boleh membaca datanya:
console.log(user.username);
tetapi tidak boleh mengubah:
user.username = "Mark";
❌ Error.
14. Readonly pada object
Misalnya:
interface UserSettings {
username: string;
email: string;
darkMode: boolean;
language: string;
}
Kita punya:
const user: Readonly<UserSettings> = {
username: "John Smith",
email: "john@gmail.com",
darkMode: false,
language: "EN"
};
Sekarang:
console.log(user.username);
✅ Boleh.
Tetapi:
user.username = "Mark";
❌ Error.
Begitu juga:
user.email = "new@gmail.com";
❌ Error.
user.darkMode = true;
❌ Error.
Semua property menjadi read-only.
15. Apa yang dilakukan Readonly<T>?
Misalnya:
interface User {
name: string;
age: number;
}
Kemudian:
Readonly<User>
secara konsep menjadi:
{
readonly name: string;
readonly age: number;
}
Jadi:
user.name = "Mark";
❌
dan:
user.age = 40;
❌
Tetapi:
console.log(user.name);
✅
16. Partial vs Readonly
Ini bagian yang paling penting untuk dihafalkan.
Partial<T> | Readonly<T> |
|---|---|
| Membuat property optional | Membuat property read-only |
| Property boleh tidak ada | Property harus tetap ada kecuali dari awal optional |
| Fokus pada keberadaan property | Fokus pada perubahan property |
| Cocok untuk update data | Cocok untuk data yang tidak boleh dimodifikasi |
name?: string | readonly name: string |
17. Analogi paling gampang
Bayangkan sebuah formulir.
Partial
“Kamu tidak harus mengisi semua kolom.”
Name: optional
Email: optional
Age: optional
Readonly
“Setelah formulir dibuat, kamu tidak boleh mengedit isinya.”
Name: 🔒
Email: 🔒
Age: 🔒
18. Bisa digabung?
Bisa.
Misalnya:
Partial<Readonly<User>>
Artinya secara konsep:
Property boleh tidak ada, dan property yang ada tidak boleh diubah.
Atau:
Readonly<Partial<User>>
Secara umum menghasilkan kombinasi yang serupa untuk property-level readonly + optional.
Contoh:
interface User {
name: string;
age: number;
}
Kemudian:
const user: Partial<Readonly<User>> = {
name: "John"
};
name:
optional + readonly
age:
optional + readonly
19. Bagaimana Partial sebenarnya bekerja?
Ini menarik karena Partial<T> sebenarnya bisa kita bayangkan dibuat menggunakan Mapped Type.
Secara konsep:
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
Jangan khawatir kalau syntax ini belum familiar.
Ini menggabungkan materi yang sudah kita pelajari:
keyof- generic
- optional property
Mari kita baca perlahan.
type MyPartial<T>
Artinya kita membuat generic type.
Kemudian:
keyof T
mengambil semua key dari T.
Misalnya:
type User = {
name: string;
age: number;
};
maka:
keyof User
adalah:
"name" | "age"
Kemudian:
[P in keyof T]
artinya:
“Untuk setiap property yang ada di T…”
Lalu:
?
membuat property tersebut optional.
Jadi:
MyPartial<User>
menghasilkan konsep:
{
name?: string;
age?: number;
}
20. Jadi Partial ternyata berhubungan dengan keyof
Ini bagus untuk menghubungkan materi-materi sebelumnya.
Kita sebelumnya belajar:
keyof T
artinya:
semua key yang dimiliki T.
Sekarang:
Partial<T>
secara konsep menggunakan key tersebut untuk membuat versi baru dari T dengan property optional.
Jadi perjalanan belajarnya mulai nyambung:
Generic
↓
keyof
↓
Mapped Type
↓
Partial
21. Bagaimana Readonly bekerja?
Readonly<T> juga secara konsep menggunakan mapped type.
Kira-kira:
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};
Misalnya:
type User = {
name: string;
age: number;
};
Maka:
MyReadonly<User>
menjadi:
{
readonly name: string;
readonly age: number;
}
Jadi Partial dan Readonly bukan sesuatu yang “ajaib”.
Mereka adalah utility types yang melakukan transformasi terhadap sebuah type.
22. Apa itu Utility Type?
Nah, istilah yang perlu kamu kenal:
Utility Type adalah built-in type yang disediakan TypeScript untuk membantu kita memanipulasi atau membuat type baru berdasarkan type yang sudah ada.
Contoh:
Partial<T>
Readonly<T>
Pick<T, K>
Omit<T, K>
Record<K, T>
Required<T>
Dan masih banyak lagi.
Lecture ini hanya membahas dua:
Partial
Readonly
23. Jangan tertukar dengan readonly biasa
Kita bisa membuat readonly tanpa generic.
Misalnya:
interface User {
readonly id: number;
name: string;
}
Artinya hanya id yang readonly.
user.id = 10;
❌ Error.
Tetapi:
user.name = "Mark";
✅ Boleh.
Sedangkan:
Readonly<User>
akan membuat semua property menjadi readonly.
Jadi:
type User = {
id: number;
name: string;
};
Jika:
Readonly<User>
maka:
{
readonly id: number;
readonly name: string;
}
24. const vs Readonly — penting!
Ini sering membingungkan.
const bukan berarti object menjadi immutable.
Misalnya:
const user = {
name: "John"
};
Kita masih bisa:
user.name = "Mark";
✅ Boleh.
Kenapa?
Karena const melarang kita mengganti variable/reference-nya, bukan property di dalam object.
Kita tidak boleh:
user = {
name: "Mark"
};
❌
Tetapi boleh:
user.name = "Mark";
✅
Kalau:
const user: Readonly<User> = {
name: "John"
};
maka:
user.name = "Mark";
❌
Jadi:
const
↓
variable/reference tidak boleh di-reassign
Readonly<T>
↓
property tidak boleh diubah melalui tipe tersebut
Ini perbedaan yang sangat penting.
25. Readonly bukan berarti deep immutable
Ini sedikit lebih advanced, tetapi bagus untuk diketahui.
Readonly<T> terutama membuat property level pertama menjadi readonly.
Misalnya:
type User = {
name: string;
address: {
city: string;
};
};
Kemudian:
const user: Readonly<User> = {
name: "John",
address: {
city: "Jakarta"
}
};
Ini tidak boleh:
user.name = "Mark";
❌
Tetapi nested property:
user.address.city = "Surabaya";
bisa tetap diperbolehkan, karena address hanya readonly sebagai property reference; object di dalamnya belum otomatis menjadi deep readonly.
Jadi:
Readonly<T>
↓
readonly level pertama
Bukan otomatis:
deep readonly semua nested object
Untuk kasus deep immutable, perlu pendekatan/type utility khusus.
26. Contoh nyata gabungan Partial dan Readonly
Misalnya aplikasi punya data konfigurasi:
interface Config {
host: string;
port: number;
debug: boolean;
}
Ketika membuat config lengkap:
const config: Config = {
host: "localhost",
port: 3000,
debug: false
};
Tetapi ketika melakukan update:
function updateConfig(
changes: Partial<Config>
) {
// ...
}
Kita bisa:
updateConfig({
debug: true
});
Tidak perlu:
updateConfig({
host: "localhost",
port: 3000,
debug: true
});
Kemudian kalau ada function yang hanya boleh membaca config:
function printConfig(
config: Readonly<Config>
) {
console.log(config.host);
console.log(config.port);
console.log(config.debug);
}
Di dalam function tersebut:
config.port = 5000;
❌ Tidak diperbolehkan.
Karena function hanya menerima config sebagai read-only.
Ini pola yang sangat berguna:
Partial
↓
data untuk UPDATE
Readonly
↓
data untuk READ
27. Kenapa Readonly berguna untuk function parameter?
Ini contoh yang bagus:
function calculateTotal(
products: Readonly<Product[]>
) {
// calculate
}
Function tersebut hanya perlu membaca product.
Dengan Readonly, kita mencegah function melakukan hal seperti:
products.push(...)
atau:
products.pop()
Jadi function tersebut secara type contract mengatakan:
“Saya hanya membaca data ini. Saya tidak akan mengubahnya.”
Ini membuat code lebih aman dan mudah dipahami.
28. Hubungkan dengan Generic yang sudah dipelajari
Sekarang kita bisa melihat bahwa:
Partial<T>
dan:
Readonly<T>
mengikuti konsep Generic yang sama.
T adalah placeholder.
Misalnya:
Partial<User>
berarti:
T = User
dan:
Readonly<User>
berarti:
T = User
Kalau:
Partial<Product>
berarti:
T = Product
Jadi sebenarnya kita sudah sangat familiar dengan konsep ini.
29. Cara membaca syntax-nya
Kalau melihat:
Partial<User>
bacalah:
“User, tetapi semua property-nya optional.”
Kalau melihat:
Readonly<User>
bacalah:
“User, tetapi semua property-nya readonly.”
Ini jauh lebih mudah daripada mencoba menghafal implementasinya.
30. Ringkasan besar lecture
Kita punya:
interface UserSettings {
username: string;
email: string;
darkMode: boolean;
language: string;
}
Normal
UserSettings
Semua wajib:
username REQUIRED
email REQUIRED
darkMode REQUIRED
language REQUIRED
Partial
Partial<UserSettings>
Semua optional:
username OPTIONAL
email OPTIONAL
darkMode OPTIONAL
language OPTIONAL
Readonly
Readonly<UserSettings>
Semua tidak boleh diubah:
username 🔒
email 🔒
darkMode 🔒
language 🔒
31. Cheat Sheet 🧠
| Syntax | Arti gampang |
|---|---|
Partial<T> | Semua property T menjadi optional |
Readonly<T> | Semua property T menjadi readonly |
Partial<User> | User dengan property optional |
Readonly<User> | User dengan property readonly |
Partial<Readonly<User>> | Optional + readonly |
const obj | Reference variable tidak bisa di-reassign |
Readonly<T> | Property tidak bisa diubah melalui tipe tersebut |
32. Contoh final yang perlu kamu kuasai
interface User {
name: string;
email: string;
age: number;
}
Object normal
const user: User = {
name: "John",
email: "john@gmail.com",
age: 30
};
Semua wajib.
Partial
const changes: Partial<User> = {
age: 31
};
Valid karena semua property optional.
Bisa juga:
const changes: Partial<User> = {
name: "Mark",
age: 25
};
Readonly
const user: Readonly<User> = {
name: "John",
email: "john@gmail.com",
age: 30
};
Baca boleh:
console.log(user.name);
Tetapi ubah tidak boleh:
user.name = "Mark"; // ❌
33. Hubungan dengan materi Generic secara keseluruhan
Kalau kita rangkum perjalanan materi Generic yang sudah kamu pelajari:
GENERIC
│
├── Generic Function
│ ↓
│ function identity<T>()
│
├── Generic Class
│ ↓
│ class ShoppingCart<T>
│
├── Generic Constraint
│ ↓
│ T extends object
│
├── keyof Constraint
│ ↓
│ U extends keyof T
│
├── Generic vs Union
│ ↓
│ T ≠ A | B
│
└── Built-in Generic / Utility Types
│
├── Partial<T>
└── Readonly<T>
Jadi Partial dan Readonly sebenarnya bukan konsep yang benar-benar baru.
Mereka adalah pemanfaatan Generic yang sudah dibuat oleh TypeScript untuk melakukan transformasi terhadap type.
🔥 Tiga kalimat yang paling penting
Kalau besok lupa semuanya, ingat tiga ini:
Partial<T>→ “Tapi property-nya boleh tidak lengkap.”
Readonly<T>→ “Datanya boleh dibaca, tapi property-nya tidak boleh diubah.”
Generic → “Saya membuat type yang fleksibel/reusable tanpa kehilangan type safety.”
Dan satu pola praktis yang sangat sering kamu temui di project TypeScript:
function updateUser(data: Partial<User>) {
// hanya perlu sebagian data
}
sedangkan:
function displayUser(user: Readonly<User>) {
// hanya membaca data, tidak boleh mengubah
}
Kalau kamu sudah memahami dua ini, kamu sudah punya fondasi yang bagus untuk masuk ke Utility Types TypeScript lainnya seperti Pick, Omit, Required, dan Record.