Discriminated union — tag field pattern
API javobi uch holat qaytaradi: yuklanmoqda, muvaffaqiyat, xato. Har birida turli xususiyatlar. in operator bilan ajratish — qaysi xususiyat noyob ekanini eslab qolish shart, yangi holat qo'shilganda unutish mumkin.
Discriminated Union — har ob'ektga bitta noyob "belgi" (tag) maydoni berish. TypeScript bu maydonga qarab barcha xususiyatlarni biladi.
Muammo — belgisiz union
type ApiHolat =
| { yuklanmoqda: boolean }
| { natija: string }
| { xabar: string };
function qayta(h: ApiHolat) {
if ("natija" in h) return h.natija;
if ("xabar" in h) return h.xabar;
// yuklanmoqda holati unutilsa TypeScript ogohlantirmaydi
}
Discriminated union — yechim
Har variantda bir xil nomli, turli literal qiymatli maydon:
type ApiHolat =
| { tur: "yuklanmoqda" }
| { tur: "muvaffaqiyat"; natija: string }
| { tur: "xato"; xabar: string };
switch bilan to'liq va xavfsiz ajratish:
function qayta(h: ApiHolat): string {
switch (h.tur) {
case "yuklanmoqda": return "Yuklanmoqda...";
case "muvaffaqiyat": return h.natija; // ✅ natija mavjud
case "xato": return h.xabar; // ✅ xabar mavjud
}
}
Agar "bekor" holati qo'shilsa va switch da ko'rilmasa — TypeScript string qaytishini talab qilib xato beradi.
To'lov tizimi misoli
type Tolov =
| { tur: "karta"; raqam: string; muddat: string }
| { tur: "naqd"; summa: number }
| { tur: "online"; email: string };
function tolovBajar(t: Tolov): string {
switch (t.tur) {
case "karta": return `Karta ${t.raqam}`;
case "naqd": return `Naqd ${t.summa} so'm`;
case "online": return `PayPal ${t.email}`;
}
}
t.tur === "karta" blokida TypeScript t.raqam va t.muddat mavjudligini biladi — boshqa holatlarda emas.
Generic API javob
type Javob<T> =
| { status: "ok"; data: T }
| { status: "xato"; xabar: string }
| { status: "kutish" };
function handle<T>(j: Javob<T>): T | null {
switch (j.status) {
case "ok": return j.data;
case "xato": console.error(j.xabar); return null;
case "kutish": return null;
}
}
Tez-tez uchraydigan xatolar
Discriminant maydoni string (keng) tur:
// ❌ — string juda keng, TypeScript ajrata olmaydi
type A = { tur: string; x: number };
type B = { tur: string; y: string };
// ✅ — literal tur
type A = { tur: "a"; x: number };
type B = { tur: "b"; y: string };
switch da barcha case yo'q:
type Holat = { tur: "a" } | { tur: "b" } | { tur: "c" };
function fn(h: Holat): string {
switch (h.tur) {
case "a": return "A";
case "b": return "B";
// "c" unutildi — TypeScript xato beradi (string qaytishi kerak)
}
}
Xulosa
// Har variantda noyob literal tag
type Shakl =
| { tur: "doira"; radius: number }
| { tur: "to'rtburchak"; en: number; bo: number }
| { tur: "uchburchak"; asos: number; h: number };
function maydoni(s: Shakl): number {
switch (s.tur) {
case "doira": return Math.PI * s.radius ** 2;
case "to'rtburchak": return s.en * s.bo;
case "uchburchak": return (s.asos * s.h) / 2;
}
}
Tag maydoni + switch = to'liq, xavfsiz, kengaytiriladigan tuzilma.
Keyingi dars
31-dars: Type predicate (is) va exhaustiveness checking (never)