10 β Type alias, intersection va kompozitsiya¶
β¬ οΈ Oldingi: 09 β any, unknown, never va void Β· π README Β· Keyingi: 11 β Generics β asoslar β‘οΈ
Bu bobda: tiplarga nom berishni va kichik tiplardan kattasini "yig'ishni" o'rganamiz.
typealias (har qanday tipga β primitiv, union, tuple, object, funksiyaga β qisqa nom) qanday ishlashini,intersection(A & Bβ IKKALA tipning ham hamma xossasi, union'ning aksi) bilan object tiplarni birlashtirishni,interface extendsvatype &farqini, generic type aliaslarni,typevainterfaceorasida qanday tanlash kerakligini va kichik bloklardan katta tip qurish (kompozitsiya) san'atini ko'rib chiqamiz.
Muammo¶
Tasavvur qiling: jamoa bilan katta loyiha yozayapsiz. Kodingizning o'nlab joyida bir xil obyekt shakli takrorlanadi:
function foydalanuvchiKorsat(u: { id: number; ism: string; email: string }) { /* ... */ }
function foydalanuvchiSaqla(u: { id: number; ism: string; email: string }) { /* ... */ }
function foydalanuvchiYangila(u: { id: number; ism: string; email: string }) { /* ... */ }
Bu yerda kamida uchta muammo bor:
- Takror. Bir xil shakl uch marta yozildi. Ertaga
emailgatelefonqo'shsangiz β uch joyni qo'lda tahrirlaysiz, bittasini unutsangiz β xato. - O'qilmaydi. Funksiya imzosi uzun va shovqinli. "Bu nima obyekt?" degan savol darrov tug'ilmaydi.
- Birlashtirib bo'lmaydi. Endi shu foydalanuvchiga "manzil ma'lumotlari" ham qo'shilsin desangiz, qanday qilasiz? Yana har joyda qo'lda yozasizmi?
JavaScript'da bunday vaziyatda biror "qolip" yo'q edi β obyekt shaklini faqat boshda ko'rib bilardingiz. TypeScript bu muammoni ikkita qurol bilan hal qiladi:
typealias β tipga nom berib, uni bir marta yozib, hamma joyda qayta ishlatish.intersection(&) β bir nechta tipni birlashtirib, yangi katta tip qurish.
Boshlaymiz.
type alias β tipga nom berish¶
type kalit so'zi bilan istalgan tipga nom (alias β taxallus) berasiz. Sintaksis sodda: type Nom = tip;
Endi yuqoridagi takror yo'qoladi:
function foydalanuvchiKorsat(u: Foydalanuvchi) { /* ... */ }
function foydalanuvchiSaqla(u: Foydalanuvchi) { /* ... */ }
function foydalanuvchiYangila(u: Foydalanuvchi) { /* ... */ }
Shaklni o'zgartirish kerak bo'lsa β faqat bitta joyni, type Foydalanuvchini tahrirlaysiz, qolgan hamma joy avtomatik yangilanadi.
π type faqat nom β u yangi qiymat yoki klass yaratmaydi. Kompilyatsiyadan keyin (TypeScript JavaScript'ga aylanganda) typelarning hammasi yo'qoladi. Ya'ni type faqat kod yozish va tekshirish paytida yashaydi, ishlab turgan dasturda undan asar ham qolmaydi.
type β faqat object emas, HAR QANDAY tip uchun¶
Eng muhim tushuncha: type faqat obyekt shakliga emas, istalgan tipga nom bera oladi:
type ID = number; // primitiv tipga nom
type Ism = string; // primitiv tipga nom
type StatusKodi = 200 | 404 | 500; // union tipga nom
type Koordinata = [number, number]; // tuple tipga nom
type Hisoblagich = (avval: number) => number; // funksiya tipiga nom
let foydalanuvchiId: ID = 42;
let kod: StatusKodi = 404;
let nuqta: Koordinata = [41.3, 69.2];
let oshir: Hisoblagich = (n) => n + 1;
β
Bu hammasi toza kompilyatsiya o'tadi. ID aslida numberning o'zi β lekin nom kodga ma'no beradi: id: ID deganda "bu shunchaki son emas, bu identifikator" degan niyat ko'rinadi.
π‘ Nomlash usuli: tip nomlarini odatda katta harf bilan boshlaydilar (Foydalanuvchi, StatusKodi), o'zgaruvchilar kichik harf bilan (foydalanuvchi). Bu kodda "bu tip-mi yoki qiymat-mi?" degan savolni darrov hal qiladi.
π type X = Y da X β Y uchun boshqa nom, alohida yangi tip emas. type ID = number yozsangiz, ID va number bir-biriga to'liq almashinadi (interchangeable). TypeScript ularni ajratmaydi: let a: ID = 5 ga oddiy number ham bemalol o'tadi. Agar haqiqatan ajratilgan "branded" tip kerak bo'lsa, bu ilg'or mavzu β hozir kerak emas.
Muammo: ikki tipni birlashtirish kerak¶
Endi asosiy muammoga qaytamiz. Ikkita mustaqil tip bor:
Sizga shunday obyekt kerak: ham manzil ma'lumotlari, ham aloqa ma'lumotlari bitta joyda bo'lsin. Qanday qilasiz? Ikkalasini qo'lda ko'chirib yangi type yozish mumkin, lekin bu yana takror. TypeScript'da maxsus operator bor β intersection (kesishma), & belgisi.
intersection (&) β ikkala tipning HAMMA xossasi¶
A & B o'qilishi: "ham A, ham B". Natija β IKKALA tipning hamma xossasini birga talab qiladigan yangi tip:
type ToliqManzil = Manzil & Aloqa;
const m: ToliqManzil = {
shahar: "Toshkent",
kocha: "Amir Temur 1",
email: "info@mail.uz",
telefon: "+998901234567",
};
β
Toza o'tadi. ToliqManzil endi to'rttala xossani β shahar, kocha, email, telefon β birga talab qiladi.
Birortasini tushirib qoldirsangiz, kompilyator darrov ushlaydi:
const m: ToliqManzil = {
shahar: "Toshkent",
kocha: "Amir Temur 1",
email: "info@mail.uz",
// β Xato: Property 'telefon' is missing in type
// '{ shahar: string; kocha: string; email: string; }'
// but required in type 'Aloqa'.
};
telefon Aloqadan keladigan majburiy xossa edi β & uni ham talab qiladi.
intersection β union'ning AKSI¶
Bu eng ko'p chalkashtiriladigan joy, shuning uchun yaxshilab ajratib olaylik. 7-bobda union (|) ni ko'rgansiz:
A | B(union) β qiymat A YOKI B bo'lishi mumkin. "Bittasini tanla".A & B(intersection) β qiymat HAM A, HAM B bo'lishi shart. "Hammasini birga qondir".
Misol bilan:
type Kichik = { a: string };
type Katta = { b: number };
type Yoki = Kichik | Katta; // a BO'LSA ham, b bo'lsa ham, ikkalasi ham bo'lsa ham
type Vaa = Kichik & Katta; // ham a, HAM b bo'lishi SHART
const x: Yoki = { a: "salom" }; // β
faqat a β yetadi
const y: Vaa = { a: "salom", b: 5 }; // β
ikkalasi ham bor β shart
π Diqqat β bu ko'pchilik uchun teskari tuyuladi: & ("va") tipni kengaytiradi (ko'proq xossa talab qiladi, shuning uchun mos keladigan obyektlar kamayadi), | ("yoki") esa imkoniyatlarni ko'paytiradi. "VA" β ko'proq talab, "YOKI" β ko'proq erkinlik.
π‘ Eslab qolish hiylasi: maktab matematikasidagi to'plamlar. Union βͺ β "birlashma" (hamma element), intersection β© β "kesishma" (umumiy elementlar). TypeScript belgilarni mantiqdan oladi: | = OR (yoki), & = AND (va).
Tuzoq: bir-biriga zid primitiv tiplarni & qilish¶
Object tiplar bilan & mantiqli. Ammo primitiv tiplarni kesishtirsangiz qiziq narsa bo'ladi:
type Imkonsiz = string & number; // "ham string, ham number" bo'lgan qiymat?
const x: Imkonsiz = "salom";
// β Xato: Type '"salom"' is not assignable to type 'never'.
Hech qaysi qiymat bir vaqtning o'zida ham string, ham number bo'lolmaydi β shuning uchun TypeScript bu tipni never (hech qachon bo'lmaydigan tip, 9-bob) deb hisoblaydi. Imkonsizga hech narsa o'tmaydi.
π Xuddi shu narsa object tiplarda bitta xossa turi to'qnashganda ham bo'ladi:
type X = { id: string };
type Y = { id: number };
type XY = X & Y; // id: string & number = never
const z: XY = { id: 5 };
// β Xato: Type 'number' is not assignable to type 'never'.
id xossasi string & number = never bo'lib qoldi β endi XYga to'g'ri keladigan obyekt yo'q. Shuning uchun & bilan tiplarni birlashtirayotganda bir xil nomli xossalar turi mos kelishiga e'tibor bering.
interface extends vs type & β solishtirish¶
5-bobda interfaceni ko'rgan edingiz. interface ham extends bilan kengaytiriladi. Demak bizda obyekt tiplarni birlashtirishning IKKI yo'li bor β qaysi birini ishlatish kerak?
1-yo'l: interface + extends:
interface Hayvon {
nomi: string;
}
interface It extends Hayvon {
zot: string;
}
const rex: It = { nomi: "Rex", zot: "Labrador" }; // β
2-yo'l: type + &:
type Hayvon2 = {
nomi: string;
};
type It2 = Hayvon2 & {
zot: string;
};
const rex2: It2 = { nomi: "Rex", zot: "Husky" }; // β
Ikkala usul amalda deyarli bir xil natija beradi: It ham, It2 ham nomi va zotni talab qiladi.
π interface bir nechta interfeysdan birato'la extends qila oladi (vergul bilan):
interface Suzuvchi { suz(): void; }
interface Uchuvchi { uch(): void; }
interface Ordak extends Hayvon, Suzuvchi, Uchuvchi {}
const d: Ordak = {
nomi: "Donald",
suz() {},
uch() {},
}; // β
uchala manbadan ham hamma narsa kerak
Asosiy farqlar¶
| Xususiyat | interface extends |
type & |
|---|---|---|
| Nimaga ishlaydi | faqat object/klass shakliga | har qanday tipga (union, primitiv ham) |
| Zid xossa bo'lsa | darrov xato beradi (aniq) | jimgina never qiladi (ko'rinmas) |
| Qayta ochib qo'shish | mumkin (declaration merging) | mumkin emas |
| Kengaytirish ohangi | "meros oladi" (OOP) | "birlashtiradi" (kompozitsiya) |
Zid xossada farq juda muhim. interface extends to'qnashuvni darrov, aniq xato bilan ko'rsatadi:
interface A {
qiymat: string;
}
interface B extends A {
qiymat: number;
}
// β Xato: Interface 'B' incorrectly extends interface 'A'.
// Types of property 'qiymat' are incompatible.
// Type 'number' is not assignable to type 'string'.
Yuqorida ko'rganimizdek, type & esa bu holatda xato bermaydi β qiymatni jimgina never qilib qo'yadi va muammo keyinroq, obyekt yozganda chiqadi. Shu sababli aniq ierarxiya kerak bo'lsa interface extends ko'proq himoyalangan.
Declaration merging β faqat interface'da¶
interfacening typeda yo'q bir xususiyati: bir xil nomli interfaceni ikki marta yozsangiz, ular birlashadi (merge bo'ladi):
interface Oyna {
kengligi: number;
}
interface Oyna {
balandligi: number;
}
const o: Oyna = { kengligi: 100, balandligi: 50 }; // β
ikkalasi ham kerak
type bilan bu mumkin emas β bir xil nomni ikki marta e'lon qilsangiz, xato:
π Declaration merging ko'pincha tashqi kutubxonalar tipini kengaytirish uchun ishlatiladi (masalan, window obyektiga yangi xossa qo'shish). Kundalik kodda u kamroq kerak bo'ladi va ba'zan tasodifan ikki marta yozib qo'ygan tipni xato deb ushlamasligi β kamchilik bo'lib chiqishi mumkin.
Generic type alias β parametrli tip¶
Ba'zan tip "deyarli bir xil", faqat ichidagi tip o'zgaradi. Masalan "qiymatni o'rab turuvchi quti" β ichida son ham, matn ham bo'lishi mumkin. Har biriga alohida type yozmaslik uchun generic (umumiy, parametrli) type alias yoziladi. Burchakli qavs ichida tip-parametr beriladi:
type Quti<T> = {
qiymat: T;
};
const sonQuti: Quti<number> = { qiymat: 5 };
const matnQuti: Quti<string> = { qiymat: "salom" };
T β bu "joy ushlovchi" (placeholder): Quti<number> yozganingizda T o'rniga number qo'yiladi. Bir tarif β cheksiz variant.
π Generics β keyingi (11-) bobning asosiy mavzusi. Hozir faqat shuni biling: type ham generic bo'la oladi, xuddi funksiya parametr olgani kabi tip ham parametr oladi.
Bir nechta parametr ham mumkin:
type Juft<A, B> = {
birinchi: A;
ikkinchi: B;
};
const juft: Juft<string, number> = { birinchi: "yosh", ikkinchi: 25 }; // β
Generic type alias ayniqsa API javoblari uchun foydali. Quyida Natija<T> β har qanday ma'lumot turi uchun "muvaffaqiyat yoki xato" qolipini bir marta tasvirlaydi:
type Natija<T> =
| { holat: "ok"; malumot: T }
| { holat: "xato"; xabar: string };
const yaxshi: Natija<number> = { holat: "ok", malumot: 100 }; // β
const yomon: Natija<number> = { holat: "xato", xabar: "topilmadi" }; // β
Bu yerda generic, union va literal tiplar birga ishlamoqda β type ularning hammasiga nom bera oladi. interface esa bunga qodir emas: u union'ni ifodalay olmaydi.
π‘ Generic'ni intersection bilan ham qo'shish mumkin β "har qanday tipga id qo'shish":
type Identifikatorli<T> = T & { id: number };
type Mahsulot = { nomi: string };
type IdliMahsulot = Identifikatorli<Mahsulot>;
const p: IdliMahsulot = { id: 1, nomi: "Telefon" }; // β
nomi + id
type vs interface β yakuniy qaror¶
Endi eng amaliy savol: yangi tip yozayotganda type yoki interface β qaysi birini tanlash? Ikkalasi ham ko'p hollarda ishlaydi, lekin har birining kuchli tomoni bor.
| Vaziyat | Tavsiya |
|---|---|
| Oddiy object shakli (model, props) | interface yoki type β ikkalasi ham yaxshi |
union tip (A \| B \| C) |
type (interface union qila olmaydi) |
| primitiv/tuple/funksiyaga nom | type (interface faqat object) |
| Kutubxona tipini kengaytirish kerak | interface (declaration merging) |
| Aniq class-ga o'xshash ierarxiya | interface + extends |
Murakkab kompozitsiya (&, mapped) |
type |
| Mapped/conditional tip (15-bob) | type (interface qila olmaydi) |
π Amaliy qoida (2026): ko'p jamoalar shunday yo'l tutadi β "oddiy obyekt shakli uchun interface, qolgan hamma narsa uchun type". Sababi: interface xato xabarlari biroz tozaroq va declaration merging imkoni bor; lekin union, intersection, mapped kabi kuchli imkoniyatlar faqat typeda. Aslida ikkalasini aralashtirib ishlatish mutlaqo normal β ular bir-biriga qarshi emas.
π‘ Eng muhimi: bittasini tanlab, loyiha bo'ylab izchil bo'ling. Birortasi "noto'g'ri" emas β chalkashlik faqat har safar tasodifan tanlaganda paydo bo'ladi.
Kompozitsiya β kichik tiplardan katta tip qurish¶
Endi hamma narsani birga qo'yamiz. Eng yaxshi amaliyot β bitta ulkan tip yozish emas, balki kichik, mustaqil, qayta ishlatiladigan bloklar yozib, ularni & bilan yig'ish. Bu kompozitsiya deb ataladi (Lego konstruktoridek).
type Ismli = { ism: string };
type Yoshli = { yosh: number };
type Manzilli = { manzil: string };
type Mijoz = Ismli & Yoshli & Manzilli & { aktiv: boolean };
const mijoz: Mijoz = {
ism: "Laylo",
yosh: 30,
manzil: "Samarqand",
aktiv: true,
}; // β
Bu yondashuvning ustunligi: Ismli, Yoshli kabi bloklar boshqa tiplarda ham qayta ishlatiladi. Masalan, ko'p modelda "yaratilgan/yangilangan vaqt" kerak bo'ladi β buni bir marta yozib, hamma joyga ulaysiz:
type Vaqtlangan = {
yaratilgan: Date;
yangilangan: Date;
};
type Tovar = { nomi: string; narx: number };
type BazaTovar = Tovar & Vaqtlangan;
const t: BazaTovar = {
nomi: "Kitob",
narx: 50000,
yaratilgan: new Date(),
yangilangan: new Date(),
}; // β
Endi Vaqtlanganni har qanday tipga ulay olasiz β Foydalanuvchi & Vaqtlangan, Buyurtma & Vaqtlangan va hokazo. Bitta tushuncha β bitta tip.
π‘ Real misol: API javobini kompozitsiya bilan qurish. "Sahifalangan ro'yxat" qolipini generic + intersection bilan bir marta tasvirlaymiz:
type Sahifalovchi = {
sahifa: number;
jami: number;
};
type Royxat<T> = {
malumotlar: T[];
} & Sahifalovchi;
const javob: Royxat<string> = {
malumotlar: ["a", "b"],
sahifa: 1,
jami: 2,
}; // β
π Kompozitsiya uslubining mohiyati: "katta tipni boshdan yozma β kichik bo'laklardan yig'". Bu kod o'qilishini, qayta ishlatishni va kelajakda o'zgartirishni keskin osonlashtiradi. Bitta blokni tuzatsangiz, undan foydalangan hamma tip avtomatik yangilanadi.
Qisqa xulosa¶
type Nom = ...har qanday tipga (primitiv, union, tuple, object, funksiya) qisqa nom beradi; kompilyatsiyadan keyin yo'qoladi.interfacefaqat object/klass shakliga nom beradi, lekin declaration merging va tozaroq ierarxiya imkonini beradi.A & B(intersection) β IKKALA tipning hamma xossasini birga talab qiladi (A | Bunion'ning aksi). "VA" ko'proq talab, "YOKI" ko'proq erkinlik.- Zid xossalarni
&qilsa βneverchiqadi (jimgina);interface extendsesa darrov aniq xato beradi. - Generic type alias (
type Quti<T> = ...) bitta qolipdan cheksiz variant hosil qiladi β API javoblari uchun ideal. - Kompozitsiya: kichik, qayta ishlatiladigan bloklar yozib,
&bilan katta tip yig'ing β Lego kabi.
Keyingi bobda generics'ni β TypeScript'ning eng kuchli quroli β chuqurroq o'rganamiz.
10-bob mashqlari¶
Quyidagi mashqlarni o'zingiz yozib, har birini tsc --noEmit --strict bilan tekshirib ko'ring. Yechimlarni o'zingiz qiling.
-
type Yosh = numbervatype Narx = numberaliaslarini yozing. Ikkalasiga ham qiymat bering va konsolda chiqaring. (typefaqat nom ekanini his qiling.) -
type Rang = "qizil" | "yashil" | "kok"union type alias yozing. Shu tipdagi o'zgaruvchiga ruxsat etilmagan qiymat ("sariq") bering va kompilyator xatosini ko'ring. -
type Koordinata = [number, number]tuple aliasini yozing va Toshkent koordinatasini saqlang. Uchinchi element qo'shib ko'ring β qanday xato chiqadi? -
type Qoshuvchi = (a: number, b: number) => numberfunksiya type aliasini yozing va shu tipga mos funksiya yarating. -
Talabaobject type aliasini yozing (ism,kurs,bahosi). Uni uchta turli funksiya parametri sifatida ishlatib, takror yo'qolganini ko'ring. -
Manzil(shahar,kocha) vaAloqa(telefon,email) tiplarini yozing.&bilanToliqProfilqiling va to'rttala xossani to'ldiring. -
6-mashqdagi
ToliqProfilga obyekt yozayotganda bitta xossani ataylab tushirib qoldiring. Kompilyator qaysi xossa yetishmasligini aytadimi β ko'ring. -
type Yoki = { a: string } | { b: number }vatype Va = { a: string } & { b: number }yozing. Har biriga mos obyekt yarating va farqni tushuntiring (izoh sifatida yozing). -
type Imkonsiz = string & numberyozing. Unga qiymat berishga urinib,neverxatosini o'z ko'zingiz bilan ko'ring. -
type X = { id: string }vatype Y = { id: number }ni&qiling. Natijadagiidnima tip bo'ladi? Obyekt yozib, xatoni ko'ring va sababini izohlang. -
interface Hayvon { nomi: string }vainterface Mushuk extends Hayvon { rangi: string }yozing.Mushuktipidagi obyekt yarating. -
11-mashqni endi
typeva&bilan qayta yozing (type Hayvon2 = ...,type Mushuk2 = Hayvon2 & ...). Natija bir xil ekanini ko'ring. -
interface A { x: string }vainterface B extends A { x: number }yozing.interface extendszid xossada qanday aniq xato berishini ko'ring. -
Bir xil nomli ikkita
interface Sozlamayozing (biritil: string, ikkinchisitema: string). Declaration merging tufayli obyektda ikkala xossa ham kerak bo'lishini ko'ring. -
Bir xil nomli ikkita
type Sozlamayozib ko'ring. Qanday xato chiqadi?interfacedan farqini izohlang. -
type Quti<T> = { qiymat: T }generic type alias yozing.Quti<number>,Quti<string>vaQuti<boolean>uchun uchta obyekt yarating. -
type Juft<A, B> = { chap: A; ong: B }ikki parametrli generic alias yozing.Juft<string, number>tipidagi obyekt yarating. -
type Natija<T> = { holat: "ok"; malumot: T } | { holat: "xato"; xabar: string }yozing.Natija<string>uchun ham "ok", ham "xato" variantiga misol yarating. -
Ismli,Yoshli,Emaillikabi uchta kichik blok tip yozing. Ularni&bilan birlashtiribFoydalanuvchitipini quring (kompozitsiya). To'liq obyekt yarating. -
type vs interfaceqaror jadvalini esga olib, quyidagi besh holatning har biriga qaysi birini tanlashingizni yozing va sababini bir jumlada izohlang: (a) union tip kerak; (b) oddiy model obyekti; (c) primitiv tipga nom; (d) kutubxona tipini kengaytirish; (e) mapped tip (15-bobni oldindan ko'rib). GenericIdentifikatorli<T> = T & { id: number }ni ham yozib, biror modelgaidqo'shib sinab ko'ring.