Yaqinda TypeScript’da kichik, lekin qiziqarli bir holatga duch keldim.
Enum bilan ishlayotganda oddiygina Object.values() orqali qiymatlarni olishni xohlardim, lekin kutilmaganda ikkita turdagi ma’lumot qaytdi: string’lar va raqamlar.
Shunda o‘yladim: “Axir Object.values() faqat qiymatlarni qaytaradi-ku, nega enum’da bunday g‘alati natija chiqyapti?”
O‘rganib chiqdim — aslida bu TypeScript’ning numeric enum mexanizmidagi maxsus xususiyat ekan.
Muammo qanday paydo bo‘ldi?
Keling, oddiy misol:
export enum StudentProgress {
TenPercent = 10,
ThirtyPercent = 30,
FiftyPercent = 50,
EightyPercent = 80,
HundredPercent = 100,
}
console.log(Object.values(StudentProgress));
Men kutgan natija shunday edi:
[10, 30, 50, 80, 100]
Lekin haqiqiy natija esa shunaqa chiqdi
[
'TenPercent',
'ThirtyPercent',
'FiftyPercent',
'EightyPercent',
'HundredPercent',
10,
30,
50,
80,
100
]
Bir qaraganda chalkash: Object.values() faqat qiymatlarni olish kerak edi-ku, nega nomlari ham qaytdi?
Sirli sabab: TypeScript’dagi Reverse Mapping
TypeScript enum’lar aslida oddiy JavaScript obyektlari sifatida ishlaydi. Numeric enum ishlatilsa, TypeScript ikki tomonlama mapping yaratadi — ya’ni qiymatdan nomni ham olish mumkin.
Keling, yuqoridagi enum qanday kompilyatsiya bo‘lishini ko‘ramiz
var StudentProgress = {
10: "TenPercent",
30: "ThirtyPercent",
50: "FiftyPercent",
80: "EightyPercent",
100: "HundredPercent",
TenPercent: 10,
ThirtyPercent: 30,
FiftyPercent: 50,
EightyPercent: 80,
HundredPercent: 100
};
Ko‘rdingmi? TypeScript har bir qiymat uchun ikki juftlik yaratgan:
"TenPercent" → 1010 → "TenPercent"
Shuning uchun Object.values() ikkala tomonni ham ko‘radi — nomlar ham, qiymatlar ham chiqadi.
Reverse Mapping nima uchun kerak?
Bu imkoniyat TypeScript enum’larini qulay qiladi. Masalan:
StudentProgress.TenPercent; // 10
StudentProgress[10]; // "TenPercent"
Demak, enum qiymatidan nomini olish ham mumkin.
Bu “reverse mapping” deb ataladi — va aynan shu sababdan Object.values() natijasi kutilmagandan uzunroq chiqadi.
Qanday qilib faqat “values” olish mumkin?
Agar bizga faqat raqamli qiymatlar kerak bo‘lsa, oddiy filter bilan hal qilish mumkin:
Object.values(StudentProgress).filter(v => typeof v === 'number');
Natija:
[10, 30, 50, 80, 100]
Yoki, agar faqat nomlar kerak bo‘lsa:
Object.values(StudentProgress).filter(v => typeof v === 'string');
Alternativa: String enum ishlatish
Agar sen reverse mapping xatti-harakatini xohlamasang, string enum ishlat.
U holda TypeScript teskari mapping yaratmaydi:
export enum StudentProgress {
TenPercent = '10',
ThirtyPercent = '30',
FiftyPercent = '50',
EightyPercent = '80',
HundredPercent = '100',
}
console.log(Object.values(StudentProgress));
// ["10", "30", "50", "80", "100"]
Natija toza va aniq bo‘ladi.
Xulosa
Bu kichik tajriba menga shuni o‘rgatdi:
- TypeScript’dagi enum’lar aslida oddiy obyektlar, lekin raqamli enum’lar teskari mapping bilan ishlaydi.
Object.values()bu mapping’ni ham ko‘radi, shu sababli natija “ikki karra” chiqadi.- Agar senga bu kerak bo‘lmasa —
string enumishlat yokitypeof v === 'number'orqali filter qil.
Bonus: “Nega bunday qilgan TypeScript jamoasi?”
TypeScript ishlab chiquvchilari bu dizaynni tanlagan, chunki u eski JavaScript’dagi enum-like pattern’larni (masalan, const Status = { 0: 'Active', Active: 0 }) moslashtirish uchun.
Ya’ni, type safety va JS mosligini birlashtirish uchun shu yondashuv tanlangan.
Shaxsiy xulosa:
“Kutilmagan natija” aslida “kutishim noto‘g‘ri bo‘lgani” sababli edi. TypeScript enum’lari haqida to‘liq tushunchaga ega bo‘lmagunimcha,
Object.values()natijasi menga g‘alati tuyuldi. Endi esa bu dizayn qarori mantiqan to‘g‘ri ekanini tushundim.