Masukkan Password
76 Property Decorator
Lecture ini membahas Property Decorator pada TypeScript, khususnya bagaimana decorator bisa digunakan untuk mengubah cara sebuah property dibaca (getter) dan diisi (setter).
Bagian yang paling penting adalah memahami hubungan antara:
Property → Decorator → Getter/Setter → target → Prototype/Constructor
Mari kita uraikan dari dasar.
1. Apa yang dipelajari?
Sebelumnya kita sudah mengenal decorator pada class:
@SomeDecorator()
class Product {
}
Sekarang decorator tidak ditempelkan pada class, tetapi pada property:
class Product {
@capitalize
name: string;
}
Jadi decorator bisa ditempatkan pada property.
Tujuannya dalam contoh lecture ini adalah:
Setiap kali kita mengambil nilai
name, huruf pertama dibuat uppercase dan karakter lainnya dibuat lowercase.
Misalnya kita memberikan:
new Product("sAMSUNG", 2400)
hasil ketika membaca:
product.name
menjadi:
Samsung
2. Class Product
Awalnya class-nya kira-kira seperti ini:
class Product {
name: string;
price: number;
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
}
Kita bisa membuat object:
const p = new Product("Samsung", 2400);
Kemudian:
console.log(p);
hasilnya:
Product {
name: "Samsung",
price: 2400
}
3. Membuat Property Decorator
Kemudian dibuat function:
function capitalize(target: any, propertyKey: string) {
}
Lalu digunakan:
class Product {
@capitalize
name: string;
price: number;
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
}
Perhatikan sesuatu yang penting.
Kita menulis:
@capitalize
bukan:
@capitalize()
Kenapa?
Karena capitalize adalah decorator function langsung, bukan decorator factory.
4. Bedakan @capitalize dan @capitalize()
Ini penting sekali.
Decorator function
function capitalize(target: any, propertyKey: string) {
}
Penggunaannya:
@capitalize
Decorator factory
Misalnya:
function logger() {
return function(target: any) {
// decorator
};
}
Penggunaannya:
@logger()
Karena:
logger()
↓
menghasilkan decorator
Sedangkan:
capitalize
↓
sudah merupakan decorator
Jadi:
@capitalize → langsung decorator
@logger() → factory → decorator
5. Property Decorator menerima 2 parameter
Ketika decorator digunakan pada property:
@capitalize
name: string;
function decorator menerima dua parameter:
function capitalize(
target: any,
propertyKey: string
) {
}
Yaitu:
target
propertyKey
6. Apa itu propertyKey?
propertyKey adalah nama property yang diberi decorator.
Contohnya:
class Product {
@capitalize
name: string;
}
Karena decorator dipasang pada:
name
maka:
propertyKey
berisi:
"name"
Misalnya kita melakukan:
console.log(propertyKey);
hasilnya:
name
7. Apa itu target?
Nah, ini bagian yang agak membingungkan.
target bergantung pada apakah property tersebut adalah:
- instance property, atau
- static property
8. Instance Property
Misalnya:
class Product {
@capitalize
name: string;
}
name adalah instance property.
Karena kita mengaksesnya melalui object:
const p = new Product(...);
p.name;
Untuk property instance, target mengarah ke prototype dari class tersebut.
Secara konsep:
Product
│
└── prototype
│
└── name
Jadi:
target
adalah prototype object.
9. Static Property
Sekarang misalnya:
class Product {
@capitalize
static productName = "Samsung";
}
Ini adalah static property.
Kita mengaksesnya melalui class:
Product.productName
bukan:
const p = new Product();
p.productName;
Untuk static property, target mengarah ke constructor function/class itu sendiri.
Secara sederhana:
Instance property
↓
target
↓
Prototype
Static property
↓
target
↓
Constructor / Class
10. Jadi target bisa apa?
Lecture menggunakan:
target: any
karena target dapat berupa:
Object
atau
Function
Untuk instance property:
target → Prototype Object
Untuk static property:
target → Constructor Function
11. Sekarang tujuan decorator-nya
Sampai di sini kita baru mengetahui:
function capitalize(target: any, propertyKey: string) {
}
Tetapi decorator tersebut belum melakukan sesuatu.
Tujuan kita adalah:
input:
"sAMSUNG"
output:
"Samsung"
Atau:
input:
"APPLE"
output:
"Apple"
Atau:
input:
"iphone"
output:
"Iphone"
Jadi kita ingin:
Huruf pertama uppercase, sisanya lowercase.
12. Kenapa menggunakan Getter dan Setter?
Kita ingin mengontrol dua hal:
Ketika property diisi
product.name = "sAMSUNG";
kita ingin menyimpan:
"samsung"
Ketika property dibaca
console.log(product.name);
kita ingin mengembalikan:
"Samsung"
Untuk mengontrol proses membaca dan menulis property, kita bisa menggunakan:
- getter
- setter
13. Membuat variable value
Di dalam decorator dibuat:
let value: string;
Variable ini digunakan untuk menyimpan nilai sebenarnya.
Bayangkan ada dua layer:
product.name
↓
getter / setter
↓
value
Jadi value menjadi tempat penyimpanan internal.
14. Membuat Getter
Kemudian dibuat:
const getter = function () {
return value.charAt(0).toUpperCase()
+ value.slice(1);
};
Mari kita pecah.
Misalnya:
value = "samsung";
15. charAt(0)
Kode:
value.charAt(0)
mengambil karakter pada index 0.
String:
s a m s u n g
↑
0
Jadi:
"samsung".charAt(0)
hasilnya:
"s"
16. .toUpperCase()
Kemudian:
value.charAt(0).toUpperCase()
berarti:
"s"
↓
"S"
Hasil:
"S"
17. slice(1)
Kemudian:
value.slice(1)
artinya:
Ambil string mulai dari index 1 sampai akhir.
Untuk:
s a m s u n g
0 1 2 3 4 5 6
slice(1) menghasilkan:
amsung
18. Digabung dengan +
Sekarang kita punya:
"S"
dan:
"amsung"
Kemudian:
"S" + "amsung"
hasilnya:
"Samsung"
Jadi getter:
const getter = function () {
return value.charAt(0).toUpperCase()
+ value.slice(1);
};
menghasilkan:
samsung
↓
Samsung
19. Kenapa slice(1) tidak perlu lowercase?
Di lecture awalnya menggunakan:
value.slice(1).toLowerCase()
Tetapi kemudian diubah menjadi:
value.slice(1)
Karena setter sudah melakukan:
newValue.toLowerCase()
Jadi ketika nilai disimpan, bagian lainnya sudah lowercase.
Misalnya:
product.name = "sAMSUNG";
setter akan menyimpan:
"samsung"
Kemudian getter hanya perlu mengubah:
samsung
↓
Samsung
20. Membuat Setter
Sekarang kita membutuhkan setter:
const setter = function (newValue: string) {
value = newValue.toLowerCase();
};
Ini berarti setiap kali kita melakukan:
product.name = "sAMSUNG";
yang sebenarnya disimpan adalah:
"samsung"
Bukan:
"sAMSUNG"
21. Alur Setter
Misalnya:
product.name = "SAMSUNG";
Prosesnya:
"SAMSUNG"
↓
setter
↓
toLowerCase()
↓
"samsung"
↓
value
Jadi value sekarang:
"samsung"
22. Kemudian ketika property dibaca
Misalnya:
console.log(product.name);
Getter dipanggil:
product.name
↓
getter
↓
value = "samsung"
↓
"s" → "S"
↓
"amsung"
↓
"Samsung"
Hasil:
Samsung
23. Mengembalikan Getter dan Setter dari Decorator
Sekarang kita mempunyai:
const getter = function () {
return value.charAt(0).toUpperCase()
+ value.slice(1);
};
const setter = function (newValue: string) {
value = newValue.toLowerCase();
};
Kemudian decorator mengembalikan object:
return {
get: getter,
set: setter
};
Secara konsep kita mengatakan kepada TypeScript:
“Untuk property ini, gunakan function
getterketika property dibaca dansetterketika property ditulis.”
24. Bentuk lengkap decorator
Secara sederhana:
function capitalize(
target: any,
propertyKey: string
): any {
let value: string;
const getter = function () {
return value.charAt(0).toUpperCase()
+ value.slice(1);
};
const setter = function (newValue: string) {
value = newValue.toLowerCase();
};
return {
get: getter,
set: setter
};
}
Kemudian:
class Product {
@capitalize
name: string;
price: number;
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
}
25. Apa yang terjadi ketika new Product()?
Misalnya:
const p = new Product("sAMSUNG", 2400);
Constructor menjalankan:
this.name = name;
Nilai name adalah:
"sAMSUNG"
Karena name memiliki decorator @capitalize, assignment tersebut diarahkan ke setter.
Setter:
newValue.toLowerCase()
menghasilkan:
"samsung"
Kemudian:
value = "samsung";
26. Ketika console.log(p.name)
Sekarang:
console.log(p.name);
Karena kita membaca property name, getter dipanggil.
Getter:
value.charAt(0).toUpperCase()
+
value.slice(1)
Nilai:
samsung
menjadi:
Samsung
Maka output:
Samsung
27. Gambaran paling sederhana
Anggap decorator ini seperti filter di antara kita dan property.
Tanpa decorator:
set:
"SAMSUNG"
↓
name
↓
"SAMSUNG"
get:
name
↓
"SAMSUNG"
Dengan @capitalize:
set:
"SAMSUNG"
↓
setter
↓
"samsung"
↓
value
get:
value = "samsung"
↓
getter
↓
"Samsung"
Jadi decorator mengubah perilaku property.
28. Kenapa ini berguna?
Misalnya aplikasi kita memiliki banyak product:
new Product("samsung", 2400);
new Product("APPLE", 3000);
new Product("soNY", 5000);
Kita ingin semua nama product memiliki format:
Samsung
Apple
Sony
Tanpa decorator, kita mungkin harus menulis logic formatting di banyak tempat.
Dengan decorator:
@capitalize
name: string;
logic tersebut bisa dipusatkan pada satu tempat.
29. Hal penting tentang target
Mari kita ulang karena ini salah satu poin utama lecture.
Misalnya:
class Product {
@capitalize
name: string;
}
name adalah instance property.
Maka:
target → Product.prototype
Sedangkan:
class Product {
@capitalize
static name2: string;
}
name2 adalah static property.
Maka:
target → Product / Constructor
Jadi:
Property Decorator
│
┌─────────┴─────────┐
│ │
Instance Static
│ │
▼ ▼
Prototype Constructor
30. Satu hal yang perlu diperhatikan
Contoh lecture ini menggunakan legacy TypeScript decorators, yang memiliki pola:
function decorator(target, propertyKey) {}
Ini penting karena TypeScript juga memiliki standard decorators dengan API yang berbeda pada versi modern.
Jadi kalau kamu mencoba kode lecture di project TypeScript yang konfigurasi/versinya berbeda, bisa saja bentuk decorator-nya tidak sama.
Untuk memahami lecture ini, fokus dulu pada model legacy decorator yang sedang digunakan:
@capitalize
name: string;
dengan:
(target, propertyKey)
31. Ringkasan besar
Ada beberapa konsep yang perlu kamu pegang.
① Property Decorator
Decorator bisa dipasang pada property:
class Product {
@capitalize
name: string;
}
② Decorator function menerima 2 argument
function capitalize(target, propertyKey) {
}
target:
- instance property → prototype
- static property → constructor/class
propertyKey:
- nama property
③ Getter
Getter dijalankan ketika property dibaca:
console.log(product.name);
Dalam contoh:
samsung → Samsung
④ Setter
Setter dijalankan ketika property diisi:
product.name = "SAMSUNG";
Dalam contoh:
SAMSUNG → samsung
⑤ Alur keseluruhan
@capitalize
│
▼
Property "name"
│
┌───────┴────────┐
│ │
SETTER GETTER
│ │
▼ ▼
name = "sAMSUNG" product.name
│ │
▼ ▼
toLowerCase() ubah huruf pertama
│ │
▼ ▼
"samsung" "Samsung"
│
▼
value
🧠 Cara mengingat lecture ini
Kalau kamu hanya ingin mengingat 5 hal, ingat ini:
1. Decorator bisa dipasang pada property.
@capitalize
name: string;
2. Property decorator menerima
targetdanpropertyKey.
function capitalize(target, propertyKey) {}
3.
propertyKeyadalah nama property.
"name"
4. Instance property →
targetadalah prototype; static property →targetadalah constructor/class.
5. Getter dipakai saat membaca property, setter dipakai saat mengisi property.
Dan inti contoh lecture ini:
SET
"SAMSUNG"
↓
setter
↓
"samsung"
↓
disimpan
GET
"samsung"
↓
getter
↓
"Samsung"
Jadi decorator di sini bukan sekadar menjalankan console.log, tetapi digunakan untuk mengubah perilaku sebuah property tanpa harus mengubah kode constructor atau setiap tempat di mana property tersebut digunakan.