5 β Object tiplari va interface¶
β¬ οΈ Oldingi: 04 β Massivlar, tuple va enum Β· π README Β· Keyingi: 06 β Funksiya tiplari β‘οΈ
Bu bobda: real dasturlarda ma'lumot ko'pincha obyekt shaklida yuradi β foydalanuvchi, mahsulot, API javobi. Shu obyektning "shaklini" (qaysi maydon bor, qaysi tipda) TypeScript'ga qanday tushuntirishni o'rganamiz: inline object type,
interface, optional?vareadonlymaydonlar, index signature[key: string],extendsbilan kengaytirish, method maydonlari,interfacevatypefarqi hamda TypeScript'ning yuragidagi g'oya β strukturaviy tiplilik (shakl mos kelsa β mos).
Muammo¶
JavaScript'da bu kod ko'p marta yozilgan:
function salomBer(odam) {
return "Salom, " + odam.ism + "! Yoshingiz " + odam.yosh;
}
salomBer({ ism: "Olim", yosh: 25 }); // "Salom, Olim! Yoshingiz 25"
salomBer({ isim: "Olim", yosh: 25 }); // "Salom, undefined! Yoshingiz 25" β
salomBer("Olim"); // "Salom, undefined! Yoshingiz undefined" β
Ikkinchi chaqiruvda ism o'rniga isim deb xato yozdik β JavaScript indamaydi, oddiygina undefined qaytaradi. Uchinchisida butunlay string berdik β yana xato yo'q, lekin natija buzuq. Bu xatolar runtime'da, ya'ni dastur foydalanuvchi qo'lida ishlayotganda chiqadi va ko'pincha allaqachon kech bo'ladi.
Muammoning ildizi shu: JavaScript funksiyaga qanday shakldagi obyekt kelishini bilmaydi. Biz odamda ism va yosh bo'lishini kutamiz, lekin bu kutish hech qayerda yozilmagan.
TypeScript aynan shu kutishni yozib qo'yish imkonini beradi. Obyektning shaklini bir marta belgilaysiz β compiler keyin har bir chaqiruvni tekshiradi:
function salomBer(odam: { ism: string; yosh: number }): string {
return `Salom, ${odam.ism}! Yoshingiz ${odam.yosh}.`;
}
salomBer({ ism: "Olim", yosh: 25 }); // β
OK
Bu { ism: string; yosh: number } β inline object type (joyida yozilgan obyekt tipi). U funksiyaga: "menga faqat ism (string) va yosh (number) bor obyekt ber" deydi. Endi xato yozsangiz, kod yozilishidayoq belgilanadi β keling, shuni qadam-baqadam ochamiz.
Inline object type β joyidagi shakl¶
Eng oddiy yo'l β tipni o'sha yerning o'zida, jingalak qavs ichida sanab yozish:
let mashina: { rusum: string; yili: number } = {
rusum: "Cobalt",
yili: 2022,
};
mashina.yili = 2023; // β
number kutiladi
Maydonlar orasini nuqtali-vergul (;) bilan ajratamiz (vergul ham ishlaydi, lekin tip ichida ; odat). Endi ataylab xato qilamiz:
interface Foydalanuvchi {
ism: string;
yosh: number;
faol: boolean;
}
// β Xato: yosh string berildi
const u: Foydalanuvchi = { ism: "Olim", yosh: "25", faol: true };
// error TS2322: Type 'string' is not assignable to type 'number'.
// β Xato: faol maydoni umuman yo'q
const u: Foydalanuvchi = { ism: "Olim", yosh: 25 };
// error TS2741: Property 'faol' is missing in type
// '{ ism: string; yosh: number; }' but required in type 'Foydalanuvchi'.
π Inline tip bitta joyda yaxshi, lekin bir xil shaklni 3-4 joyda takrorlasangiz β { ism: string; yosh: number; faol: boolean } ni har safar qayta yozish charchatadi va xatoga olib keladi. Aynan shu yerda interface kerak bo'ladi.
interface β nomli obyekt shakli¶
interface β obyekt shakliga nom berish usuli. Bir marta yozasiz, keyin shu nomni hamma joyda ishlatasiz. Buni obyekt bilan dastur o'rtasidagi shartnoma deb tushuning: "menga shu interface'ga mos obyekt bermasangiz, kelishmaganmiz".
interface Foydalanuvchi {
ism: string;
yosh: number;
faol: boolean;
}
function malumotKorsat(u: Foydalanuvchi): void {
console.log(`${u.ism}, ${u.yosh} yosh, faol: ${u.faol}`);
}
const u1: Foydalanuvchi = { ism: "Olim", yosh: 25, faol: true };
malumotKorsat(u1); // β
E'tibor bering: interface nomi odatda katta harf bilan boshlanadi (Foydalanuvchi, Mahsulot) β bu tip ekanini ko'rsatadi.
π‘ interface β faqat compile (kod mashina o'qiydigan JavaScript'ga aylantirilishi) bosqichidagi narsa. Yaratilgan JavaScript faylida interfacedan asar ham qolmaydi β u runtime'da hech qanday kod chiqarmaydi, faqat compiler'ga yo'l-yo'riq beradi. Demak, interface tezlikni sekinlashtirmaydi: u shunchaki yozuv paytidagi "tekshiruvchi".
π interface so'ngida nuqtali-vergul (;) yoki vergul kerak emas β ko'pchilik uni ; bilan tugatmaydi:
Optional maydon β ?¶
Har doim ham hamma maydon majburiy emas. Masalan, profilga telefon kiritish ixtiyoriy bo'lishi mumkin. Maydon nomidan keyin ? qo'ysangiz β u optional (bo'lishi shart emas) bo'ladi:
interface Profil {
ism: string;
telefon?: string; // bo'lishi ham, bo'lmasligi ham mumkin
}
const p1: Profil = { ism: "Ali" }; // β
telefon yo'q
const p2: Profil = { ism: "Vali", telefon: "+99890..." }; // β
telefon bor
telefon? ning haqiqiy tipi β string | undefined. Ya'ni u yo string, yo umuman yo'q (undefined). Shuning uchun unga to'g'ridan-to'g'ri murojaat qilolmaysiz:
function uzunlik(p: Profil): number {
// β Xato: telefon undefined bo'lishi mumkin
return p.telefon.length;
// error TS18048: 'p.telefon' is possibly 'undefined'.
}
Compiler haq: agar p1 kabi telefoni yo'q obyekt kelsa, undefined.length runtime'da dasturni qulatardi. To'g'ri yo'l β avval tekshirish (bu narrowing, 8-bobda chuqur o'rganamiz):
function profilChop(p: Profil): void {
console.log(p.ism);
if (p.telefon !== undefined) {
console.log("Telefon: " + p.telefon.trim()); // β
bu yerda telefon β string
} else {
console.log("Telefon kiritilmagan");
}
}
π telefon?: string va telefon: string | undefined deyarli bir xil ko'rinadi, lekin farqi bor: birinchisida maydonni umuman yozmasangiz ham bo'ladi ({ ism: "Ali" }); ikkinchisida maydon majburiy β uni hech bo'lmasa telefon: undefined deb yozishingiz shart.
readonly β faqat o'qiladigan maydon¶
Ba'zi maydonlar bir marta o'rnatiladi va keyin o'zgarmaydi β masalan, yozuvning idsi. Maydon oldiga readonly qo'ysangiz, uni yaratishdan keyin o'zgartirib bo'lmaydi:
interface Hisob {
readonly id: number;
balans: number;
}
const h: Hisob = { id: 1, balans: 1000 };
h.balans = 500; // β
balans o'zgaruvchan
// β Xato: id ni o'zgartirib bo'lmaydi
h.id = 99;
// error TS2540: Cannot assign to 'id' because it is a read-only property.
π readonly ham faqat compile bosqichidagi himoya. JavaScript'da Object.freeze kabi runtime qulflash emas β yaratilgan JS faylda maydon oddiy o'zgaruvchan bo'lib qoladi. Maqsadi: siz xato bilan o'zgartirib qo'ymasligingiz. Bu ko'pincha kifoya.
π‘ Massiv yoki obyektni butunlay "muzlatish" uchun readonly Foydalanuvchi[] yoki ReadonlyArray<...> ham bor β bularni 14-bobdagi utility types bilan kengroq ko'ramiz.
Index signature β noma'lum nomli maydonlar¶
Hozirgacha maydon nomlarini oldindan bilardik (ism, yosh). Lekin ba'zan maydon nomlari oldindan noma'lum bo'ladi β masalan, lug'at: kalit har qanday so'z, qiymat esa tarjima. Mana shu yerda index signature (indeks imzosi) yordamga keladi:
interface Lugat {
[soz: string]: string; // "kaliti string bo'lgan har qanday maydon β qiymati string"
}
const tarjima: Lugat = {
kitob: "book",
qalam: "pen",
};
tarjima["daftar"] = "notebook"; // β
yangi kalit qo'shsak ham bo'ladi
console.log(tarjima.kitob); // "book"
[soz: string] ichidagi soz β shunchaki nom, istalgan so'z yozsa bo'ladi ([kalit: string]). Muhimi β kalit tipi (string) va qiymat tipi (oxirgi string).
Endi qiymat tipiga mos kelmasa, xato chiqadi:
// β Xato: index signature qiymati string bo'lishi kerak edi
const t: Lugat = { kitob: "book", soni: 5 };
// error TS2322: Type 'number' is not assignable to type 'string'.
Index signature'ni aniq maydonlar bilan aralashtirsa ham bo'ladi. Faqat aniq maydon tipi index signature qiymat tipiga sig'ishi shart:
interface Ball {
ism: string; // aniq maydon
[fan: string]: string | number; // qolgan barcha maydonlar
}
const talaba: Ball = { ism: "Sardor", matematika: 90, fizika: 85 }; // β
π Bu yerda ism tipi (string) index signature tipiga (string | number) sig'gani uchun ishladi. Agar ism: boolean desangiz, xato chiqardi β chunki har bir aniq maydon ham "qolgan hamma maydon" qoidasiga bo'ysunishi kerak.
π‘ Amalda noma'lum shakldagi ma'lumot uchun ko'pincha Record<string, string> (utility type, 14-bob) yozish qulayroq va o'qilishi oson. Lekin index signature ostida aynan shu mexanizm ishlaydi.
Method va funksiya maydonlari¶
Obyekt ichida funksiya bo'lishi mumkin. interface'da uni ikki xil yozish mumkin β ikkalasi ham to'g'ri:
interface Kalkulyator {
natija: number;
qoshish(son: number): void; // method sintaksisi
ayirish: (son: number) => void; // funksiya-maydon sintaksisi
}
const k: Kalkulyator = {
natija: 0,
qoshish(son: number): void {
this.natija += son;
},
ayirish(son: number): void {
this.natija -= son;
},
};
k.qoshish(5);
k.ayirish(2);
console.log(k.natija); // 3
qoshish(son: number): void β bu obyektga method sifatida funksiya borligini bildiradi. ayirish: (son: number) => void β interface'da maydonga funksiya tipi bergan; obyektda esa uni shu method sintaksisi bilan amalga oshirdik. Farqi nozik, lekin amalda muhim: method sintaksisida this obyektga (kga) bog'lanadi, shuning uchun this.natija ishlaydi. Agar ayirishni arrow funksiya ((son) => { this.natija -= son }) bilan yozsangiz, this obyektga emas, tashqi (global) qiymatga bog'lanadi va compiler xato beradi β shu sababli obyekt method'larida arrow emas, method sintaksisini ishlating. Funksiya tiplari va this haqida 6-bobda batafsil to'xtalamiz.
interface'ni kengaytirish β extends¶
Tasavvur qiling: It ham, Mushuk ham jonzot β ikkalasida nom va yosh bor. Bu umumiy maydonlarni har safar qayta yozmaslik uchun extends (kengaytirish) ishlatamiz. Bola interface ota-interface'ning hamma maydonini meros qilib oladi va ustiga o'zinikini qo'shadi:
interface Jonzot {
nom: string;
yosh: number;
}
interface It extends Jonzot {
zot: string; // It'da qo'shimcha maydon
}
const bobik: It = { nom: "Bobik", yosh: 3, zot: "alabay" }; // β
uchchala maydon ham kerak
Bitta interface bir nechta interface'dan ham meros olishi mumkin β ularni vergul bilan sanaysiz:
interface Vaqt {
yaratilgan: Date;
}
interface IdLi {
id: number;
}
interface Maqola extends Vaqt, IdLi {
sarlavha: string;
}
const m: Maqola = { id: 1, yaratilgan: new Date(), sarlavha: "TS bilan ishlash" };
// β
Maqola = Vaqt + IdLi + o'ziniki
π‘ extends bilan kichik, qayta ishlatiladigan interface'lardan kattalarini "yig'asiz". Bu β DRY (Don't Repeat Yourself β o'zingni takrorlama) tamoyilining tip darajasidagi ko'rinishi.
Declaration merging β interface'ning maxsus xususiyati¶
interface'da g'alati, lekin foydali bir narsa bor: bir nomli interface'ni ikki marta e'lon qilsangiz, TypeScript ularni xato deb hisoblamaydi β aksincha, birlashtiradi (declaration merging):
interface Sozlama {
til: string;
}
interface Sozlama {
qorongi: boolean;
}
// Endi Sozlama'da ikkala maydon ham bor:
const s: Sozlama = { til: "uz", qorongi: true }; // β
π Bu kundalik kodda kamdan-kam kerak bo'ladi va ataylab ishlatish odatda chalkashlik tug'diradi. Lekin u mavjud tiplarni kengaytirish uchun juda qulay β masalan, kutubxona bergan tipga o'z maydoningizni qo'shish (17- va 21-boblarda .d.ts fayllar bilan ko'ramiz). Hozircha shuni bilib qo'ying: bu β interfaceda bor, typeda yo'q xususiyat.
interface vs type alias β qaysi birini tanlash?¶
4-bobda type (type alias β tipga taxallus) bilan tanishgansiz. Obyekt shakli uchun type ham ishlaydi:
type Manzil = {
shahar: string;
kocha: string;
};
const adr: Manzil = { shahar: "Toshkent", kocha: "Amir Temur" }; // β
Ko'p hollarda interface va type bir-birini almashtira oladi. Ikkalasi ham obyekt shaklini bera oladi, extends/& bilan kengaytiriladi. Unda farqi nimada?
| interface | type alias | |
|---|---|---|
| Obyekt shakli | β | β |
| Kengaytirish | extends |
& (intersection) |
Union (A \| B) |
β | β |
| Primitive/tuple'ga nom | β | β
(type Id = number) |
| Declaration merging | β | β |
type qila oladigan, interface qila olmaydigan asosiy narsa β union va boshqa murakkab tiplar:
// β
Bu faqat type bilan mumkin:
type Holat = "kutilmoqda" | "tasdiqlandi" | "bekor";
type Id = number | string;
const holat: Holat = "tasdiqlandi";
interface da esa extends va declaration merging bor.
π‘ Amaliy qoida (2026):
- Obyekt yoki klass shakli, ayniqsa kengaytiriladigan / kutubxona uchun ochiq tip β
interface.- Union, literal, tuple, yoki murakkab tip kompozitsiyasi β
type.- Ikkalasi ham yarasa β bitta loyihada bittasini tanlab, izchil ishlating.
Bularning ortidagi type imkoniyatlari (intersection &, union) bilan 7-, 9- va 10-boblarda chuqurroq ishlaymiz. Hozircha: obyekt shakli uchun ikkalasi ham yaraydi.
Strukturaviy tiplilik β TypeScript'ning yuragi¶
Endi eng muhim g'oya. Ba'zi tillar (masalan, Java) tiplarni nomi bo'yicha solishtiradi: tip Nuqta deb atalmasa, u Nuqta emas. TypeScript esa boshqacha β u shaklni solishtiradi. Buni strukturaviy tiplilik (structural typing) deyiladi: agar obyektning shakli kerakli shaklga mos kelsa, nomi nima bo'lishidan qat'i nazar, u mos hisoblanadi.
interface Nuqta {
x: number;
y: number;
}
function masofa(n: Nuqta): number {
return Math.sqrt(n.x * n.x + n.y * n.y);
}
// koordinata Nuqta deb e'lon qilinmagan, lekin x va y bor β va qo'shimcha nom:
const koordinata = { x: 3, y: 4, nom: "A" };
console.log(masofa(koordinata)); // β
5 β shakl mos kelgani uchun ishlaydi
koordinata hech qaerda Nuqta deb belgilanmagan, hatto qo'shimcha nom maydoni ham bor. Lekin unda x: number va y: number borligi uchun u Nuqta "shartnomasini" qondiradi β demak masofaga mos keladi. "x va y bor β boshqasi muhim emas."
π Lekin bitta tuzoq bor. Obyektni to'g'ridan-to'g'ri literal (joyida yozilgan { ... }) sifatida bersangiz, TypeScript qo'shimcha maydonlarga qattiqroq qaraydi β buni excess property check (ortiqcha maydon tekshiruvi) deyiladi:
// β Xato: literal'da ortiqcha maydon
const n: Nuqta = { x: 3, y: 4, nom: "A" };
// error TS2353: Object literal may only specify known properties,
// and 'nom' does not exist in type 'Nuqta'.
Nega yuqorida koordinata o'tdi-yu, bu o'tmadi? Chunki koordinata avval o'zgaruvchiga yozilgan, keyin uzatilgan β TypeScript faqat shaklni solishtirdi. Bu yerda esa { ... } bevosita Nuqtaga berildi β compiler bunday holatda "ehtimol nomni xato yozgandirsiz yoki interface'ga maydon qo'shishni unutgandirsiz" deb ogohlantiradi. Bu β foydali himoya, terish xatolarini ushlaydi.
π‘ Strukturaviy tiplilikning ulkan foydasi: tiplar bir-biriga "yopishib" qolmaydi. Bir funksiyaga turli joydan kelgan, har xil nomdagi, lekin shakli mos obyektlarni berolasiz β bu kodingizni moslashuvchan qiladi.
Hammasini birlashtirib β kichik misol¶
Mana o'rganganlarimizning birgalikdagi ko'rinishi β kichik do'kon mahsuloti:
interface Olchov {
birlik: "dona" | "kg" | "litr";
miqdor: number;
}
interface Mahsulot extends Olchov {
readonly id: number;
nom: string;
narx: number;
chegirma?: number; // ixtiyoriy
}
function yakuniyNarx(m: Mahsulot): number {
const chegirma = m.chegirma ?? 0; // chegirma bo'lmasa 0 (?? β JS'dan tanish)
return m.narx * m.miqdor * (1 - chegirma / 100);
}
const olma: Mahsulot = {
id: 1,
nom: "Olma",
narx: 12000,
birlik: "kg",
miqdor: 3,
chegirma: 10,
};
console.log(yakuniyNarx(olma)); // 32400
Mahsulot β Olchovni kengaytiradi (birlik, miqdor meros), readonly id o'zgarmaydi, chegirma? ixtiyoriy, birlik esa literal union (faqat shu uch qiymatdan biri). Bularning hammasi bitta tabiiy modelda birlashdi.
JavaScript'da obyektlar bilan ishlash asoslarini takrorlamoqchi bo'lsangiz β JavaScript kitobidagi obyektlar bo'limiga qayting. Bu yerda biz faqat ularga tip qo'shishni ko'rdik.
5-bob mashqlari¶
Quyidagi mashqlarni o'zingiz yozib bajaring. Har birini tsc --noEmit --strict bilan tekshirib ko'ring β toza misollar xatosiz o'tsin, "xato chiqishi kerak" deganlarida compiler aynan kutilgan xabarni bersin.
-
Kitobnomli interface yozing:sarlavha(string),betlar(number),sotuvda(boolean) maydonlari bilan. Unga mos bitta obyekt yarating. -
Yuqoridagi
Kitobtipidagi obyektni qabul qilib, "<sarlavha>β<betlar>bet" qaytaradigantavsiffunksiyasini yozing. -
Inline object type ishlating:
markazfunksiyasi{ x: number; y: number }qabul qilib, ikkalasining o'rtacha qiymatini chop etsin (interface yaratmang). -
Foydalanuvchiinterface'igaemail?optional maydon qo'shing. Email bor bo'lsa uni chop etadigan, bo'lmasa "email yo'q" deydigan funksiya yozing (narrowing bilan). -
Sozlamainterface'idareadonly versiya: numbermaydoni bo'lsin. Obyekt yarating vaversiyani o'zgartirishga urinib ko'ring β qanday xato chiqishini yozib oling (TS2540). -
readonlyva?ni bitta interface'da birlashtiring:Buyurtmadareadonly id(number),izoh?(string) vasumma(number) bo'lsin. -
Telefon kitobi uchun index signature yozing:
Telefonlarinterface'ida kalit β ism (string), qiymat β raqam (string). Uchta yozuv qo'shing. -
Ballarinterface'ida[fan: string]: numberindex signature bo'lsin. Bitta talabaning ballarini yozib,matematikabalini chop eting. -
8-mashqdagi
Ballarga aniqism: stringmaydon qo'shishga urinib ko'ring. Nega xato chiqadi?[fan: string]: number | stringqilib tuzating. -
Mahsulotinterface'iganarxniHisobla(soni: number): numbermethod e'lon qiling. Obyekt yarating va methodni chaqiring. -
Hayvoninterface'i (nom: string) ni yozib, undanextendsbilanQush(qo'shimchaucha_oladi: boolean) interface'ini hosil qiling. -
extendsbilan ikki interface'dan meros oling:Vaqt(yaratilgan: Date) vaMuallifli(muallif: string) danMaqolani (sarlavha: string) hosil qiling. -
Bir nomli
Konfiginterface'ini ikki marta e'lon qiling (declaration merging): birinchisidatil: string, ikkinchisidatema: string. Ikkala maydonli obyekt yarating. -
Bir xil
Nuqtashaklini avvalinterface, keyintypebilan yozing. Ikkalasi ham bir xil obyektga mos kelishini tekshiring. -
interfaceqila olmaydigan,typeqila oladigan misol yozing:Yonalishdeb"shimol" | "janub" | "sharq" | "garb"literal union yarating. -
JavobHolatinitypebilan"yuklanmoqda" | "muvaffaqiyat" | "xato"qilib yozing. Shu tipdagi o'zgaruvchini har uch qiymatga navbatma-navbat o'rnating. -
Strukturaviy tiplilikni ko'rsating:
Isminterface'i (ism: string) ni qabul qiladigan funksiya yozing. Ungaismva qo'shimchayoshmaydoni bor, oldindan o'zgaruvchiga yozilgan obyekt bering β o'tishini ko'ring. -
Excess property check'ni "ushlang": 17-mashqdagi funksiyaga obyektni endi to'g'ridan-to'g'ri literal sifatida (qo'shimcha maydon bilan) bering. Qanday xato chiqadi (
TS2353)? -
Kichik loyiha:
Foydalanuvchi(readonly id,ism,email?) va undanextendsqiluvchiAdmin(huquqlar: string[]) interface'larini yozing. Bitta admin obyekti yarating. -
Yakuniy: 19-mashqdagi
Foydalanuvchilardan iborat massiv (Foydalanuvchi[]) tuzing, ulardan emaili bor bo'lganlarinifilterbilan ajratib, ismlarini chop eting. Optional maydonni xavfsiz tekshirishni unutmang.