TypeScript

Interfeys va tip aliaslari

Haqiqiy dasturlarda biz ko'pincha oddiy sonlar yoki matnlar emas, balki murakkab obyektlar bilan ishlaymiz โ€” foydalanuvchi, buyurtma, mahsulot. TypeScript bunday obyektlarning "shaklini" tavsiflash uchun ikkita asosiy vosita beradi: interface (interfeys) va type alias (tip aliasi). Ushbu darsda ularni chuqur o'rganamiz.

interface โ€” obyekt shaklini tavsiflash

Interfeys โ€” obyekt qanday xossalarga va qanday turlarga ega bo'lishi kerakligini tavsiflaydigan "shartnoma". Uni interface kalit so'zi bilan e'lon qilamiz:

interface Foydalanuvchi {
  ism: string;
  yosh: number;
  faol: boolean;
}

Endi bu interfeysni tur sifatida ishlatishimiz mumkin. Obyekt interfeysga to'liq mos kelishi shart:

let user: Foydalanuvchi = {
  ism: "Ali",
  yosh: 25,
  faol: true
};

Agar biror xossani tushirib qoldirsak yoki noto'g'ri tur bersak, kompilyator darrov xato ko'rsatadi:

let user: Foydalanuvchi = {
  ism: "Ali",
  yosh: 25
};
// Xato: Property 'faol' is missing in type
// '{ ism: string; yosh: number; }' but required in type 'Foydalanuvchi'.
Interfeys nomlarini odatda katta harf bilan boshlaymiz (masalan Foydalanuvchi, Mahsulot). Bu โ€” kelishuv (konvensiya), majburiy emas, lekin kodni o'qishni osonlashtiradi.

type alias โ€” tur uchun taxallus

Tip alias (type alias) โ€” bu istalgan turga nom berish usuli. Uni type kalit so'zi bilan yaratamiz. Obyekt shakli uchun u interfeysga juda o'xshaydi:

type Foydalanuvchi = {
  ism: string;
  yosh: number;
  faol: boolean;
};

let user: Foydalanuvchi = {
  ism: "Ali",
  yosh: 25,
  faol: true
};

Lekin type faqat obyektlar uchun emas โ€” u istalgan turga nom berishi mumkin. Masalan, ibtidoiy turlar, union yoki kortejlar uchun:

type Yosh = number;
type Ism = string;
type Koordinata = [number, number];
type Holat = "tayyor" | "kutilyapti";
Interfeys faqat obyekt shakllari uchun ishlatiladi. type esa ancha keng โ€” u har qanday turga (union, kortej, ibtidoiy) nom bera oladi.

Ixtiyoriy xossalar (?)

Ba'zi xossalar majburiy bo'lmasligi mumkin. Bunday xossa nomidan keyin savol belgisi ? qo'yamiz. Bu "bu xossa bo'lishi ham, bo'lmasligi ham mumkin" degani:

interface Foydalanuvchi {
  ism: string;
  yosh: number;
  email?: string; // ixtiyoriy
}

// email'siz ham to'g'ri:
let u1: Foydalanuvchi = { ism: "Ali", yosh: 25 };

// email bilan ham to'g'ri:
let u2: Foydalanuvchi = { ism: "Vali", yosh: 30, email: "vali@mail.uz" };

Ixtiyoriy xossaning turi aslida string | undefined bo'ladi โ€” ya'ni u yo bor, yo undefined. Shuning uchun uni ishlatishdan oldin tekshirish yaxshi odat:

function emailKorsat(u: Foydalanuvchi): void {
  if (u.email) {
    console.log(u.email.toUpperCase());
  } else {
    console.log("Email yo'q");
  }
}

readonly xossalar

Ba'zi xossalarni yaratilgandan keyin o'zgartirib bo'lmasligi kerak. Bunday xossa oldiga readonly yozamiz. Bu JavaScript'dagi constning obyekt xossalari uchun o'xshashi:

interface Foydalanuvchi {
  readonly id: number;
  ism: string;
}

let user: Foydalanuvchi = { id: 1, ism: "Ali" };

user.ism = "Vali"; // OK
user.id = 2;
// Xato: Cannot assign to 'id' because it is a read-only property.
readonly faqat kompilyatsiya paytida ishlaydi. Tayyor JavaScript'da bunday cheklov qolmaydi โ€” bu shunchaki dasturchi uchun xavfsizlik qatlami.

Union turlar (|)

Union (birlashma) tur โ€” bu "bir nechta turdan biri" degani. Turlar orasiga vertikal chiziq | qo'yamiz. Bu "yoki" ma'nosini beradi:

let qiymat: string | number;
qiymat = "matn"; // OK
qiymat = 42;      // OK
qiymat = true;    // Xato: boolean ruxsat etilmagan

Union tur bilan ishlashda TypeScript sizni turni tekshirishga undaydi. Chunki har bir turda mavjud amallar farq qiladi. Buni tur torayishi (narrowing) deyiladi:

function idKorsat(id: string | number): void {
  if (typeof id === "string") {
    // bu blokda id โ€” string
    console.log(id.toUpperCase());
  } else {
    // bu blokda id โ€” number
    console.log(id.toFixed(2));
  }
}

Intersection turlar (&)

Intersection (kesishma) tur โ€” union'ning aksi. U bir nechta turni birlashtirib, hammasini birdaniga talab qiladi. Bunda ampersand & ishlatiladi:

interface Ism {
  ism: string;
}

interface Yosh {
  yosh: number;
}

// ikkala interfeysni birlashtiradi:
type Odam = Ism & Yosh;

let odam: Odam = {
  ism: "Ali",
  yosh: 25
};
// ikkala xossa ham majburiy

Ya'ni Ism & Yosh โ€” bu ism va yosh xossalariga ega bo'lgan obyekt. Union "yoki" bo'lsa, intersection "va" degani.

Eslab qolish uchun: | (union) โ€” "yo bu, yo u"; & (intersection) โ€” "ham bu, ham u".

interface va type: farqi qanday?

Ikkalasi ham obyekt shaklini tavsiflay oladi, shuning uchun ko'pincha almashtirsa bo'ladi. Lekin bir nechta muhim farq bor:

Interfeysning extends orqali meros olishi:

interface Hayvon {
  ism: string;
}

interface It extends Hayvon {
  zot: string;
}

let it: It = { ism: "Rex", zot: "Ovcharka" };

Interfeysning deklaratsiya birlashuvi โ€” bir nomdagi ikkita interfeys avtomatik birlashadi:

interface Oyna {
  eni: number;
}

interface Oyna {
  bo'yi: number;
}

// endi Oyna'da ikkala xossa bor:
let o: Oyna = { eni: 100, bo'yi: 200 };
Amaliy maslahat: obyekt shakllari uchun ko'pincha interface tavsiya etiladi (ayniqsa kutubxona/API tavsiflarida). Union, kortej yoki murakkab turlar uchun esa type ishlatiladi. Ko'p loyihalar shu qoidaga amal qiladi.

Xulosa