Mundarija (36)
- Bu darsda
- 1. Nega bu kerak?
- 2. interface extends: "hammasi va yana bular"
- 2.1 Bitta ota turdan kengaytirish
- 2.2 Bola turni ota tur o'rnida ishlatish
- 2.3 Bir nechta ota tur
- 2.4 Ziddiyat — darhol xato
- 3. Kesishma (&): turlarni "va" bilan birlashtirish
- 3.1 type uchun extends yo'q
- 3.2 Joyida kesishma: nom bermasdan
- 3.3 interface va type aralash
- 4. Kesishmadagi ziddiyat: jim never
- 4.1 Xato e'londa emas, ishlatishda chiqadi
- 4.2 Butun tur never bo'lib qolganda
- 4.3 Mos ziddiyat — tur torayadi
- 4.4 extends yoki & — qaysi biri?
- 5. Declaration merging: bir nomli interfeyslar birlashadi
- 5.1 Ikki e'lon — bitta tur
- 5.2 Birlashishda ham ziddiyat bo'lmasin
- 5.3 Global turlarni kengaytirish: declare global
- 5.4 vazifalar dagi haqiqiy misol
- 5.5 Module augmentation: boshqa modul turini kengaytirish
- 6. TypeScript ushlamaydigan tuzoq: e'lon bor, qiymat yo'q
- 7. Ko'p uchraydigan xatolar
- 7.1 | o'rniga &
- 7.2 Bola turda maydon turini o'zgartirish
- 7.3 Modul ichida interface Window
- 7.4 Bir nomni tasodifan ikki marta e'lon qilish
- 7.5 never ni ko'rib, sababini qidirmaslik
- 8. Mashqlar
- 1-mashq (oson): Qaysi maydonlar majburiy?
- 2-mashq (o'rta): Takrorni yo'qoting
- 3-mashq (qiyin): Amaliy tajriba — menyu va bonus tizimi
- 9. Real ishda
- Xulosa
- Manbalar
TypeScript extends va kesishma (&): turlarni kengaytirish va declaration merging
Qisqacha: Umumiy maydonlarni bir marta yozib, keyin kengaytirasiz:
interface Dish extends MenuItem { … }yokitype Dish = MenuItem & { … }. Ikkalasi ham "MenuItem'ning hamma maydoni va yana bular" degani. Bir xil nomli ikkiinterfaceesa o'zi birlashadi (declaration merging) — kutubxona turlariga maydon qo'shish shu bilan qilinadi.
Bu darsda
interface … extendsbilan bitta yoki bir nechta turdan kengaytirasiz.- Kesishma (
&) bilantypelarni birlashtirasiz vaextendsdan farqini tushuntira olasiz. - Ziddiyatli xususiyat qachon darhol xato, qachon jim
neverbo'lishini bilasiz. - Declaration merging,
declare globalva module augmentation bilan mavjud turga maydon qo'shasiz. - TypeScript ushlamaydigan tuzoqni taniysiz: e'lon qilingan, lekin yo'q global qiymat.
Oldin bilishingiz kerak: type va interface, Obyekt turlari, null va undefined bilan xavfsiz ishlash, Meros: extends va super.
1. Nega bu kerak?
O'tgan darsda qiymat bo'lmasligi mumkin bo'lgan joylarni turlar bilan himoya qildik. Endi boshqa muammo — bir xil maydonlarning ko'p joyda takrorlanishi.
«Bahor» menyusida taomlar va ichimliklar bor. Ikkalasining ham nomi va narxi bor, taomning yana pishish vaqti, ichimlikning esa issiq-sovuqligi. Sardor ichimlik obyektini yozayotganda price o'rniga cost deb yozib qo'ydi:
const osh = { name: "Osh", price: 35000, cookMinutes: 40 };
const tea = { name: "Ko'k choy", cost: 5000, hot: true };
const order = [osh, tea];
const total = order.reduce((sum, item) => sum + item.price, 0);
console.log(total); // NaNtea.price — undefined, 35000 + undefined esa NaN. Kassa "NaN so'm" ko'rsatadi, JavaScript esa hech narsa demaydi.
TypeScript'da ham xato qilish mumkin: Dish va Drink turlarini alohida-alohida yozsangiz, birida price, ikkinchisida cost bo'lib qoladi. Haqiqiy yechim — umumiy qismni bir marta yozish va undan kengaytirish. Kundalik o'xshatish: blankaning umumiy qismi (F.I.Sh., sana) bir xil, har bo'lim faqat o'z qatorlarini qo'shadi.
2. interface extends: "hammasi va yana bular"
2.1 Bitta ota turdan kengaytirish
Klasslardagi meros kabi, interface ham boshqa interfeysdan kengaytirilishi (extends) mumkin. Bola tur otaning hamma maydonini oladi va o'zinikini qo'shadi:
interface MenuItem {
name: string;
price: number;
}
interface Dish extends MenuItem {
cookMinutes: number;
}
const osh: Dish = { name: "Osh", price: 35000, cookMinutes: 40 };
console.log(osh.name, osh.price); // Osh 35000Dish da uchta majburiy maydon bor: name va price — MenuItem dan, cookMinutes — o'zidan. Bittasini tushirib qoldiramiz:
interface MenuItem {
name: string;
price: number;
}
interface Dish extends MenuItem {
cookMinutes: number;
}
const osh: Dish = { name: "Osh", price: 35000 };Kompilyator:
TS2741: Property 'cookMinutes' is missing in type '{ name: string; price: number; }' but required in type 'Dish'.Tarjimasi: "{ name: string; price: number; } turida cookMinutes xususiyati yo'q, lekin Dish turida u majburiy". Xabar ikki turni yonma-yon qo'yadi: chapda siz bergan obyektning shakli, o'ngda kutilgan tur.
Endi «Nega bu kerak?» bo'limidagi xato qayta takrorlanmaydi. Drink ham MenuItem dan kengaytirilsa, price ni cost deb yozish imkonsiz: price ota turda bir marta yozilgan.
2.2 Bola turni ota tur o'rnida ishlatish
Dish — MenuItem ning hamma maydoni bor tur. Shuning uchun Dish ni MenuItem kutilgan joyga bemalol berish mumkin. Bu Strukturaviy tiplash darsidagi qoida: shakl mos kelsa — tur mos.
interface MenuItem {
name: string;
price: number;
}
interface Dish extends MenuItem {
cookMinutes: number;
}
interface Drink extends MenuItem {
hot: boolean;
}
function priceTag(item: MenuItem): string {
return `${item.name}: ${item.price} so'm`;
}
const manti: Dish = { name: "Manti", price: 30000, cookMinutes: 45 };
const tea: Drink = { name: "Ko'k choy", price: 5000, hot: true };
console.log(priceTag(manti)); // Manti: 30000 so'm
console.log(priceTag(tea)); // Ko'k choy: 5000 so'mpriceTag faqat name va price ni biladi. U taomni ham, ichimlikni ham qabul qiladi, ortiqcha maydonlar unga xalaqit bermaydi.
2.3 Bir nechta ota tur
interface bir vaqtda bir nechta turdan kengaytirilishi mumkin — vergul bilan:
interface MenuItem {
name: string;
price: number;
}
interface Dish extends MenuItem {
cookMinutes: number;
}
interface Drink extends MenuItem {
hot: boolean;
}
interface Combo extends Dish, Drink {}
const lunch: Combo = {
name: "Osh va choy",
price: 38000,
cookMinutes: 40,
hot: true,
};
console.log(lunch.cookMinutes, lunch.hot); // 40 trueCombo — kompleks tushlik: ham taom, ham ichimlik. U Dish va Drink ning hamma maydonini oldi. name va price ikkala otada ham bor, lekin turlari bir xil — muammo yo'q. {} — bola tur o'zidan hech narsa qo'shmadi, bu ham to'g'ri yozuv.
Klasslarda bunday qilib bo'lmaydi: JavaScript klassi faqat bitta klassdan meros oladi. interface esa faqat shakl, unda bu cheklov yo'q.
Turlar orasidagi bog'lanishni chizib ko'ramiz. O'q "kengaytiradi" degani:
flowchart BT
D["Dish<br/>+ cookMinutes"] --> M["MenuItem<br/>name, price"]
R["Drink<br/>+ hot"] --> M
C["Combo"] --> D
C --> RPastdagi tur yuqoridagining hamma maydonini oladi. Combo ikki yo'l bilan MenuItem ga yetadi, lekin name va price unda bir martadan bo'ladi.
2.4 Ziddiyat — darhol xato
Bola tur ota maydonining turini o'zgartira olmaydi. Masalan, narxni satr qilib qo'ysak:
interface MenuItem {
name: string;
price: number;
}
interface PrintedItem extends MenuItem {
price: string;
}Kompilyator:
TS2430: Interface 'PrintedItem' incorrectly extends interface 'MenuItem'.
Types of property 'price' are incompatible.
Type 'string' is not assignable to type 'number'.Tarjimasi: "PrintedItem interfeysi MenuItem ni noto'g'ri kengaytiradi. price xususiyatining turlari mos emas: string ni number ga berib bo'lmaydi". Zanjir yuqoridan pastga torayadi: avval qaysi interfeys, keyin qaysi maydon, oxirida aniq sabab.
Lekin turni toraytirish mumkin. Masalan, narxi doim 35 000 bo'lgan osh — 35000 literal turi number ning bir qismi:
interface MenuItem {
name: string;
price: number;
}
interface FixedOsh extends MenuItem {
price: 35000;
}
const osh: FixedOsh = { name: "Osh", price: 35000 };
console.log(osh.price); // 35000Qoida: bola turdagi maydon ota turdagi maydonga mos bo'lishi kerak. 35000 — son, demak mos. string — son emas, demak mos emas.
Ikki ota turning o'zi bir-biriga zid bo'lsa ham, tsc darhol to'xtatadi:
interface CashPrice {
price: number;
}
interface TextPrice {
price: string;
}
interface Mixed extends CashPrice, TextPrice {}Kompilyator:
TS2320: Interface 'Mixed' cannot simultaneously extend types 'CashPrice' and 'TextPrice'.
Named property 'price' of types 'CashPrice' and 'TextPrice' are not identical.Tarjimasi: "Mixed bir vaqtning o'zida CashPrice va TextPrice ni kengaytira olmaydi: ularning price nomli xususiyati bir xil emas".
Tekshirib ko'ring:
interface Drink extends MenuItem { price: 5000 | 7000 }— xato beradimi?interface Drink extends MenuItem { name?: string }chi?
Javob
Birinchisi — xatosiz. 5000 | 7000 — ikki sonli literal union, har biri number ga mos, demak tur toraydi. Ikkinchisi — TS2430: otada name majburiy edi, bolada ixtiyoriy bo'lsa, string | undefined bo'ladi. undefined esa string ga mos emas. Bola tur va'dani faqat kuchaytira oladi, bo'shashtira olmaydi.
3. Kesishma (&): turlarni "va" bilan birlashtirish
3.1 type uchun extends yo'q
type va interface darsida ko'rdingiz: type — har qanday turga nom berish. Unda extends so'zi yo'q. O'rniga kesishma (intersection) ishlatiladi — & belgisi:
type MenuItem = {
name: string;
price: number;
};
type Dish = MenuItem & { cookMinutes: number };
const lagmon: Dish = {
name: "Lag'mon",
price: 28000,
cookMinutes: 25,
};
console.log(lagmon.name, lagmon.cookMinutes); // Lag'mon 25A & B — "ham A, ham B": ikkala turning hamma maydoni bor qiymat. Union (A | B) esa "A yoki B" edi. Belgilarni mantiqiy amallardan eslab qoling: && — "va", || — "yoki".
Nega "kesishma" deyiladi, maydonlar esa qo'shiladi-ku? Tur — qiymatlar to'plami. MenuItem — nomi va narxi bor hamma obyektlar. { cookMinutes: number } — pishish vaqti bor hamma obyektlar. Ikkala to'plamga bir vaqtda kiradigan obyektlarda esa uchala maydon ham bor. To'plamlar kesishmasi kichrayadi, maydonlar ro'yxati esa uzayadi.
Maydon yetishmasa, xabar biroz boshqacha:
type MenuItem = { name: string; price: number };
type Dish = MenuItem & { cookMinutes: number };
const osh: Dish = { name: "Osh", price: 35000 };Kompilyator:
TS2322: Type '{ name: string; price: number; }' is not assignable to type 'Dish'.
Property 'cookMinutes' is missing in type '{ name: string; price: number; }' but required in type '{ cookMinutes: number; }'.Birinchi qator — umumiy: "obyektni Dish ga berib bo'lmaydi". Ikkinchisi — sabab: cookMinutes kesishmaning ikkinchi qismida ({ cookMinutes: number; }) majburiy. interface bilan xabar qisqaroq edi, chunki Dish bitta nomli tur. Kesishmada tsc qaysi qismda muammo borligini ko'rsatadi.
3.2 Joyida kesishma: nom bermasdan
& ning qulay tomoni — uni turga nom bermasdan, to'g'ridan-to'g'ri ishlatish mumkin. Masalan, bazaga yozilgan narsalarda id bo'ladi:
type MenuItem = { name: string; price: number };
type Saved = { id: number };
function label(item: MenuItem & Saved): string {
return `#${item.id} ${item.name}`;
}
const manti = { id: 7, name: "Manti", price: 30000 };
console.log(label(manti)); // #7 MantiSaved — kichik "qo'shimcha" tur. Uni istalgan turga ulash mumkin: MenuItem & Saved, Guest & Saved. Bunday kichik turlarni ba'zan mixin turi deyishadi — "aralashtiriladigan bo'lak".
3.3 interface va type aralash
Ikkalasi bir-biri bilan ishlaydi. interface obyekt shaklidagi type ni kengaytira oladi, & esa interface larni birlashtira oladi:
type Priced = { price: number };
interface Dish extends Priced {
name: string;
}
type DishOnSale = Dish & { discountPercent: number };
const deal: DishOnSale = {
name: "Osh",
price: 35000,
discountPercent: 20,
};
console.log(deal.price * (1 - deal.discountPercent / 100)); // 28000Faqat bitta cheklov bor. interface faqat aniq maydonlari ma'lum obyekt turidan kengaya oladi — union'dan emas:
type Payment =
| { method: "naqd" }
| { method: "karta"; last4: string };
interface PaidOrder extends Payment {
total: number;
}Kompilyator:
TS2312: An interface can only extend an object type or intersection of object types with statically known members.Tarjimasi: "Interfeys faqat obyekt turini yoki maydonlari oldindan (statik) ma'lum obyekt turlari kesishmasini kengaytira oladi". Payment — ikki variantdan biri, ya'ni maydonlari aniq emas: last4 bormi, yo'qmi — variantga bog'liq. Bu holatda & ishlaydi: type PaidOrder = Payment & { total: number } — "har ikki to'lov varianti, ikkalasiga ham total qo'shilgan".
Tekshirib ko'ring:
type A = { name: string }; type B = { price: number };—A | BvaA & Bturiga{ name: "Osh" }mos keladimi?
Javob
A | B ga — mos: "A yoki B", bu obyekt A ning talabini bajaradi. A & B ga — mos emas: "ham A, ham B", demak price ham majburiy. tsc TS2322 beradi va price yetishmasligini aytadi.
4. Kesishmadagi ziddiyat: jim never
4.1 Xato e'londa emas, ishlatishda chiqadi
interface extends da ziddiyat e'lon qilingan joyning o'zida xato edi (TS2430). Kesishmada esa tsc e'londa hech narsa demaydi:
type WithPrice = { price: number };
type WithTextPrice = { price: string };
type Broken = WithPrice & WithTextPrice;
const item: Broken = { price: 35000 };Kompilyator:
TS2322: Type 'number' is not assignable to type 'never'.type Broken = … qatori xatosiz o'tdi. Xato faqat to'rtinchi qatorda chiqdi va unda price ning turi — never. never — "hech qanday qiymat bo'la olmaydi" turi. Nega? price ham number, ham string bo'lishi kerak: number & string. Bir vaqtda son ham, satr ham bo'ladigan qiymat yo'q — kesishma bo'sh, ya'ni never.
Xabar sababni aytmaydi — faqat oqibatni ko'rsatadi. Shuning uchun never ni kutmagan joyda ko'rsangiz, birinchi savol: "bu tur qaysi kesishmadan yasalgan?"
4.2 Butun tur never bo'lib qolganda
Ziddiyat discriminated union dagi ajratuvchi maydonda bo'lsa, tsc yana ham qat'iyroq — butun obyekt turi never ga aylanadi:
type NewOrder = { status: "new"; id: number };
type ReadyOrder = { status: "ready"; id: number };
type Both = NewOrder & ReadyOrder;
const first: NewOrder = { status: "new", id: 1 };
const order: Both = first;Kompilyator:
TS2322: Type 'NewOrder' is not assignable to type 'never'.
The intersection 'Both' was reduced to 'never' because property 'status' has conflicting types in some constituents.Tarjimasi: "Both kesishmasi never ga qisqardi, chunki status xususiyati qismlarda ziddiyatli turlarga ega". Buyurtma bir vaqtda ham "yangi", ham "tayyor" bo'la olmaydi. Bu safar xabar sababni ham aytdi.
Odatda bu xato | o'rniga & yozilganda chiqadi. Niyat "yangi yoki tayyor buyurtma" edi — NewOrder | ReadyOrder.
4.3 Mos ziddiyat — tur torayadi
Ikki tur bir-biriga zid emas, biri ikkinchisining ichida bo'lsa, kesishma torroq turni oladi:
type MenuItem = { name: string; price: number };
type FixedPrice = { price: 35000 };
type FixedOsh = MenuItem & FixedPrice;
const osh: FixedOsh = { name: "Osh", price: 35000 };
console.log(osh.price + 5000); // 40000number & 35000 — bu 35000: "son va aynan 35000" faqat 35000 ning o'zi. Bu interface dagi toraytirish bilan bir xil natija, faqat boshqa yozuv.
4.4 extends yoki & — qaysi biri?
| Savol | interface … extends |
type … & |
|---|---|---|
| Ziddiyat qachon ko'rinadi? | e'londa, TS2430 |
ishlatishda, never |
| Union'ni kengaytirish | mumkin emas (TS2312) |
mumkin |
| Nom bermasdan, joyida | yo'q | bor: (a: A & B) |
| Declaration merging | bor (pastda) | yo'q |
Amaliy qoida, ko'p jamoalarda shunday:
- Obyekt shakllari ierarxiyasi (
MenuItem→Dish) —interface extends. Xato erta va aniq chiqadi. TypeScript hujjatlari ham katta loyihalarda shuni tavsiya qiladi:tscnomli interfeyslar orasidagi munosabatni bir marta hisoblab, eslab qoladi. - Bir martalik birlashma, union bilan ishlash, kichik qo'shimcha (
MenuItem & Saved) —&.
Loyihangizda type tanlangan bo'lsa (vazifalar dagi kabi — type va interface darsidagi sabab bilan), & dan bemalol foydalaning. Faqat never tuzog'ini yodda tuting.
Tekshirib ko'ring:
type T = { qty: number } & { qty: 1 | 2 | 3 }—qtyning turi qanday?{ qty: 4 }mos keladimi?
Javob
qty — 1 | 2 | 3: son va shu uch qiymatdan biri. { qty: 4 } mos emas — 4 son, lekin 1 | 2 | 3 ichida yo'q. tsc TS2322: Type '4' is not assignable to type '1 | 2 | 3'. beradi.
5. Declaration merging: bir nomli interfeyslar birlashadi
5.1 Ikki e'lon — bitta tur
type va interface darsida qisqa ko'rgan edik: bir nomli ikki interface e'loni xato emas — ular birlashadi. Bu declaration merging (e'lonlarni birlashtirish) deyiladi. Endi uni chuqurroq ko'ramiz — qachon foydali va qayerda tuzoq:
interface Order {
id: number;
}
interface Order {
total: number;
}
const order: Order = { id: 1, total: 63000 };
console.log(order.id, order.total); // 1 63000tsc ikkala e'lonni bitta Order ga yig'di: { id: number; total: number }. Endi { id: 1 } ning o'zi yetmaydi:
interface Order {
id: number;
}
interface Order {
total: number;
}
const order: Order = { id: 1 };Kompilyator:
TS2741: Property 'total' is missing in type '{ id: number; }' but required in type 'Order'.TypeScript aytyapti: «{ id: number; } da total yo'q, Order da esa u majburiy» — total ikkinchi interface Order dan keldi.
type bilan esa bir nomni ikki marta e'lon qilib bo'lmaydi:
type Guest = { name: string };
type Guest = { phone: string };Kompilyator:
TS2300: Duplicate identifier 'Guest'.
TS2300: Duplicate identifier 'Guest'.Tarjimasi: "takrorlangan nom (identifikator) Guest". Xato ikki marta chiqadi — har bir e'lon uchun bittadan. type — bitta joyda bir marta beriladigan nom. interface — "ochiq" e'lon, uni boshqa joyda davom ettirish mumkin.
5.2 Birlashishda ham ziddiyat bo'lmasin
Ikkinchi e'lon bor maydonning turini o'zgartira olmaydi:
interface Order {
id: number;
}
interface Order {
id: string;
}Kompilyator:
TS2717: Subsequent property declarations must have the same type. Property 'id' must be of type 'number', but here has type 'string'.Tarjimasi: "Keyingi xususiyat e'lonlari bir xil turda bo'lishi kerak. id number turida bo'lishi shart, bu yerda esa string". Xabardagi ikki bo'sh joy (type. Property) — xato emas, tsc shunday chiqaradi.
Bitta faylda bir nomli ikki interface yozish — odatda xato belgisi. Siz bilmasdan bir nom ikki marta berilgan bo'lishi mumkin. Unda tsc jim birlashtiradi. Declaration merging ning haqiqiy joyi — boshqa birovning turiga maydon qo'shish. Buni keyingi bo'limlarda ko'ramiz.
5.3 Global turlarni kengaytirish: declare global
Brauzerdagi window obyektining turi — Window interfeysi. U TypeScript'ning DOM turlarida (lib.dom.d.ts) e'lon qilingan. Aytaylik, sayt ilova versiyasini window.appVersion ga yozadi:
const version = window.appVersion;Kompilyator:
TS2339: Property 'appVersion' does not exist on type 'Window & typeof globalThis'.Tarjimasi: "Window & typeof globalThis turida appVersion xususiyati yo'q". Ko'rdingizmi, window ning turi ham kesishma ekan: brauzer oynasi (Window) va global obyekt (globalThis) birlashmasi.
Birinchi urinish — xuddi shu nomli interfeys yozish:
interface Window {
appVersion: string;
}
const version = window.appVersion;Kompilyator:
TS2339: Property 'appVersion' does not exist on type 'Window & typeof globalThis'.O'sha xato! Sababi: faylda import yoki export bo'lsa, u modul (ES modullar), uning ichidagi nomlar esa faylning o'ziga tegishli. Bizning bloklarimiz ham, mashqlar papkasidagi fayllar ham modul (tsconfig dagi moduleDetection: "force"). Shuning uchun bu Window — yangi, mahalliy interfeys, global Window bilan birlashmadi.
Global turga yetish uchun declare global { … } bloki kerak:
declare global {
interface Window {
appVersion: string;
}
}
console.log(window.appVersion.toUpperCase());declare — "bu narsa boshqa joyda bor, men faqat turini aytyapman" degani: JavaScript kodi yaratmaydi. global — "global nomlar maydoniga". Endi bu Window global Window bilan birlashdi. (Bu blok brauzer kodi, window Node'da yo'q. Uni TS Playground'da sinab ko'ring: xato yo'qoladi.)
5.4 vazifalar dagi haqiqiy misol
vazifalar loyihasi Service Worker'ni Trusted Types siyosatidan o'tgan manzil bilan ro'yxatdan o'tkazadi (XSS va DOM xavfsizligi dagi himoya). TypeScript'ning DOM turlarida register() faqat string | URL qabul qiladi. Loyihada .ts ga ko'chish oxirida (trusted-types.d.ts, TypeScript va DOM qadami) shunday birlashtirish yozilgan:
// Declaration merging: lib.dom dagi interfeysga yangi overload
// qo'shiladi — register() siyosatdan o'tgan manzilni ham qabul qiladi
interface ServiceWorkerContainer {
register(
manzil: string | URL | TrustedScriptURL,
sozlama?: RegistrationOptions,
): Promise<ServiceWorkerRegistration>;
}Bu .d.ts fayl — faqat turlar, import/export siz, shuning uchun u global va declare global kerak emas. .d.ts fayllarni Declaration fayllari darsida to'liq o'rganamiz. Hozir g'oyaga qarang: kutubxona turini tahrirlamasdan, unga yangi imkoniyat qo'shildi. Bir xil nomli metod ikkinchi marta yozilsa, u overload (qo'shimcha chaqiruv shakli) bo'ladi — Function overload darsida.
5.5 Module augmentation: boshqa modul turini kengaytirish
Global emas, modul eksport qilgan interfeysga maydon qo'shish ham mumkin. Bu module augmentation (modulni kengaytirish) deyiladi. Yozuvi — declare module "modul-nomi" { … }:
// loyalty.ts — menu.ts ga tegmasdan, MenuItem ga maydon qo'shadi
import type { MenuItem } from "./menu.ts";
declare module "./menu.ts" {
interface MenuItem {
bonusPoints?: number;
}
}import type — faqat turni import qilish: build'da bu qator butunlay o'chadi (Modullar TypeScript'da darsida batafsil). Bu kod ikki faylni talab qiladi. Shuning uchun u haqiqiy tsc bilan 3-mashqda sinaladi — o'sha yerda declare module siz nima bo'lishini ham ko'rasiz.
Real loyihalarda module augmentation asosan kutubxonalar uchun yoziladi. Masalan, Express (Node'dagi server kutubxonasi, Express + TypeScript darsida ko'rasiz) so'rov obyektiga user maydonini qo'shish: declare module "express" { interface Request { user?: User } }. Hozir bilish shart emas — g'oya bir xil.
Tekshirib ko'ring: Nega
typebilan declaration merging ishlamaydi va bu yaxshimi yoki yomonmi?
Javob
type — bitta e'londa to'liq aniqlanadigan nom, ikkinchi e'lon TS2300 beradi. Bu ikki tomonlama: yaxshi — tasodifan bir nomni ikki marta berib, jim birlashtirib yubormaysiz; yomon — kutubxona turini tashqaridan kengaytirib bo'lmaydi. Shuning uchun kutubxonalar kengaytiriladigan turlarni ko'pincha interface bilan yozadi.
6. TypeScript ushlamaydigan tuzoq: e'lon bor, qiymat yo'q
declare — va'da. U tsc ga "bu narsa bor" deydi, lekin hech narsa yaratmaydi. Va'da bajarilmasa-chi?
declare global {
var bahorConfig: { serviceRate: number };
}
const rate = globalThis.bahorConfig.serviceRate;
console.log(rate * 100);tsc — xatosiz: biz bahorConfig global o'zgaruvchisini e'lon qildik (var — global o'zgaruvchi globalThis ning xususiyati bo'lishi uchun, let/const bunday bo'lmaydi). Node esa:
TypeError: Cannot read properties of undefined (reading 'serviceRate')Hech kim globalThis.bahorConfig = … yozmagan. Tur "obyekt bor" deydi, haqiqatda esa undefined. Bu JSDoc darsidagi qoidaning o'zi: turlar faqat tekshiruvda yashaydi, ishlash paytida ular yo'q. tsc declare ni tekshirmaydi — unga ishonadi.
Xuddi shu xavf Window kengaytmasida ham bor: window.appVersion ni e'lon qildingiz, lekin index.html dagi skript uni o'rnatmasa — undefined. Himoya:
- Global qiymatni ixtiyoriy deb e'lon qiling (
appVersion?: string) —tschar ishlatishda tekshirishga majbur qiladi (nullvaundefineddarsidagi??va toraytirish). - Yoki global o'rniga modul eksportidan foydalaning:
import { APP_VERSION } from "./config.ts"— bu qiymat haqiqatan bor bo'ladi.
7. Ko'p uchraydigan xatolar
7.1 | o'rniga &
"Yangi yoki tayyor buyurtma" — NewOrder | ReadyOrder. & yozilsa, tur never ga qisqaradi va har qanday qiymat xato beradi (TS2322 … type 'never'). Tuzatish: ma'noni ovoz chiqarib o'qing: "va" — &, "yoki" — |.
7.2 Bola turda maydon turini o'zgartirish
interface PrintedItem extends MenuItem { price: string } — TS2430. Tuzatish: boshqa nom bering (priceText: string) yoki Omit bilan maydonni olib tashlab, qayta qo'shing (Utility types darsida).
7.3 Modul ichida interface Window
declare global siz yozilgan interface Window — mahalliy yangi interfeys, global bilan birlashmaydi. Tuzatish: declare global { interface Window { … } } yoki import/export siz .d.ts fayl.
7.4 Bir nomni tasodifan ikki marta e'lon qilish
Ikkita interface Order — xatosiz birlashadi, maydonlar kutilmaganda ko'payadi. Tuzatish: o'z kodingizda bir nom — bir e'lon. Merging faqat begona (kutubxona, global) turni kengaytirish uchun.
7.5 never ni ko'rib, sababini qidirmaslik
Type 'number' is not assignable to type 'never' — "qiymat noto'g'ri" emas, "tur imkonsiz" degani. Tuzatish: o'zgaruvchi turiga hover qiling va qaysi & ziddiyat berganini toping.
8. Mashqlar
1-mashq (oson): Qaysi maydonlar majburiy?
interface Guest {
name: string;
}
interface Booking extends Guest {
people: number;
}
type VipBooking = Booking & { table: number };
const b: VipBooking = { name: "Dilshod aka", people: 4, table: 7 };
console.log(Object.keys(b).length); // 3VipBooking da nechta majburiy maydon bor: [:3]. type Wrong = Booking & { people: string } dagi people turi: [:never]. type Booking = … deb yozilgan ikkinchi e'lon qaysi xato kodini beradi: .
Yechim
- Uchta:
name(Guestdan),people(Bookingdan),table(kesishmadan). number & string—never: bir vaqtda son ham, satr ham bo'ladigan qiymat yo'q. E'lon xatosiz, xato faqat qiymat berilganda chiqadi.TS2300: Duplicate identifier 'Booking'—interfacedan keyin bir nomlitypeham birlashmaydi. Birlashish faqatinterface+interfaceda.
2-mashq (o'rta): Takrorni yo'qoting
Bu kodda uchta tur bir xil maydonlarni takrorlaydi va bitta funksiya ishlamaydi. tsc beradigan xatoni avval o'zingiz toping (Playground'da oching):
type Staff = { name: string; phone: string };
type Cook = { name: string; phone: string; dishes: string[] };
type Waiter = { name: string; phone: string; tables: number[] };
type HeadCook = Cook & { phone: number };
function call(person: Staff): string {
return `${person.name}: ${person.phone}`;
}
const head: HeadCook = {
name: "Jasur aka",
phone: "+998 90 000 00 00",
dishes: ["Osh"],
};
console.log(call(head));Xato:
TS2322: Type 'string' is not assignable to type 'never'.Vazifa: (1) Staff ni interface qiling, Cook va Waiter uni extends bilan kengaytirsin; (2) HeadCook — Cook ga since: number (qachondan bosh oshpaz) qo'shsin, phone ni o'zgartirmasin; (3) call ni Cook ham, Waiter ham qabul qilishini tekshiring.
Yechim
Xato sababi — HeadCook dagi phone: number: Cook da u string, kesishmada string & number = never. E'lon jim o'tdi, xato faqat obyekt yozilganda chiqdi.
interface Staff {
name: string;
phone: string;
}
interface Cook extends Staff {
dishes: string[];
}
interface Waiter extends Staff {
tables: number[];
}
interface HeadCook extends Cook {
since: number;
}
function call(person: Staff): string {
return `${person.name}: ${person.phone}`;
}
const head: HeadCook = {
name: "Jasur aka",
phone: "+998 90 000 00 00",
dishes: ["Osh"],
since: 2019,
};
const waiter: Waiter = {
name: "Sardor",
phone: "+998 90 000 00 00",
tables: [1, 2],
};
console.log(call(head)); // Jasur aka: +998 90 000 00 00
console.log(call(waiter)); // Sardor: +998 90 000 00 00Endi HeadCook da phone: number yozib ko'rsangiz, xato e'lonning o'zida chiqadi — TS2430 va aniq sabab bilan. call Staff kutadi, bola turlarning hammasi unga mos.
3-mashq (qiyin): Amaliy tajriba — menyu va bonus tizimi
Mashq Tur annotatsiyasi darsida tayyorlangan kurs/mashqlar/15/ papkasida bajariladi. 16-kengaytirish/menu.ts faylini yarating:
MenuItem(readonly name,price), undanDish(cookMinutes) vaDrink(hot) —interface extendsbilan.type DishOnSale = Dish & { discountPercent: number }vasalePrice(item)— chegirmali narx, butun so'mgacha (Math.round). Foiz 0–100 dan tashqarida —RangeError.describe(item: Dish | Drink)— taom uchun"Osh — 40 daqiqa", ichimlik uchun"Ko'k choy — issiq". Ishora:"cookMinutes" in item—inbilan toraytirish.
Keyin 16-kengaytirish/loyalty.ts: bonus tizimi MenuItem ga ixtiyoriy bonusPoints?: number qo'shsin, lekin menu.ts ni o'zgartirmasdan. bonusFor(item) — bonusPoints bo'lsa o'shani, bo'lmasa har 1000 so'mga 1 ball qaytarsin. Avval declare module siz urinib ko'ring:
// 16-kengaytirish/loyalty.ts — birinchi urinish
import type { MenuItem } from "./menu.ts";
interface MenuItem {
bonusPoints?: number;
}
export function bonusFor(item: MenuItem): number {
return item.bonusPoints ?? Math.floor(item.price / 1000);
}npx tsc --pretty false ni ishga tushiring. Keyin 16-kengaytirish/menu.test.ts da testlar yozing: chegirma, noto'g'ri foiz, describe ikki variant uchun, bonusFor ikki holat uchun.
Yechim
Birinchi urinishda bizda (tsc 7.0.2) shunday chiqdi:
16-kengaytirish/loyalty.ts(2,15): error TS2440: Import declaration conflicts with local declaration of 'MenuItem'.
16-kengaytirish/loyalty.ts(9,46): error TS2339: Property 'price' does not exist on type 'MenuItem'.Birinchisi: "import e'loni MenuItem ning mahalliy e'loni bilan to'qnashadi". Bu interface Window dagi holatning o'zi — modul ichidagi interface yangi, mahalliy tur yaratadi. U import qilingan MenuItem bilan birlashmaydi, balki uni "to'sib" qo'yadi. Shuning uchun ikkinchi xato: mahalliy MenuItem da faqat bonusPoints bor, price yo'q.
// 16-kengaytirish/menu.ts — «Bahor» menyusi
export interface MenuItem {
readonly name: string;
price: number;
}
export interface Dish extends MenuItem {
cookMinutes: number;
}
export interface Drink extends MenuItem {
hot: boolean;
}
// Aksiyadagi taom: Dish va chegirma foizi
export type DishOnSale = Dish & { discountPercent: number };
export function salePrice(item: DishOnSale): number {
const percent = item.discountPercent;
if (percent < 0 || percent > 100) {
throw new RangeError(`Chegirma 0–100 bo'lsin: ${percent}`);
}
return Math.round(item.price * (1 - percent / 100));
}
export function describe(item: Dish | Drink): string {
if ("cookMinutes" in item) {
return `${item.name} — ${item.cookMinutes} daqiqa`;
}
return `${item.name} — ${item.hot ? "issiq" : "sovuq"}`;
}// 16-kengaytirish/loyalty.ts — bonus tizimi (menu.ts ga tegmaydi)
import type { MenuItem } from "./menu.ts";
// Module augmentation: menu.ts dagi MenuItem ga maydon qo'shiladi
declare module "./menu.ts" {
interface MenuItem {
bonusPoints?: number;
}
}
// Bonus berilmagan bo'lsa — har 1000 so'mga 1 ball
export function bonusFor(item: MenuItem): number {
return item.bonusPoints ?? Math.floor(item.price / 1000);
}// 16-kengaytirish/menu.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { salePrice, describe, type Dish } from "./menu.ts";
import { bonusFor } from "./loyalty.ts";
const osh: Dish = { name: "Osh", price: 35000, cookMinutes: 40 };
test("salePrice: 20% chegirma", () => {
assert.equal(salePrice({ ...osh, discountPercent: 20 }), 28000);
});
test("salePrice: noto'g'ri foiz — RangeError", () => {
const bad = { ...osh, discountPercent: 120 };
assert.throws(() => salePrice(bad), RangeError);
});
test("describe: taom va ichimlik", () => {
assert.equal(describe(osh), "Osh — 40 daqiqa");
const tea = { name: "Ko'k choy", price: 5000, hot: true };
assert.equal(describe(tea), "Ko'k choy — issiq");
});
test("bonusFor: maxsus ball yoki narxdan", () => {
assert.equal(bonusFor(osh), 35);
assert.equal(bonusFor({ ...osh, bonusPoints: 50 }), 50);
});import { …, type Dish } — Dish faqat tur, type so'zi uni build'da o'chiriladigan qiladi (tsconfig dagi verbatimModuleSyntax buni talab qiladi). { ...osh, discountPercent: 20 } — spread bilan yangi obyekt: uning turi Dish maydonlari va discountPercent, ya'ni DishOnSale ga mos.
Bir nozik joy: module augmentation butun loyihaga ta'sir qiladi. loyalty.ts ni import qilmagan faylda ham MenuItem da bonusPoints turi ko'rinadi, chunki tsc papkadagi hamma faylni birga tekshiradi. Shuning uchun kengaytmani alohida, nomi aniq faylda saqlang.
npx tsc --pretty false — chiqishsiz (chiqish kodi 0). node --test 16-kengaytirish/menu.test.ts bizda (Node 24.21.0) shunday chiqdi:
✔ salePrice: 20% chegirma (3.0187ms)
✔ salePrice: noto'g'ri foiz — RangeError (1.3558ms)
✔ describe: taom va ichimlik (0.3662ms)
✔ bonusFor: maxsus ball yoki narxdan (0.2804ms)
ℹ tests 4
ℹ suites 0
ℹ pass 4
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 327.052XATOLAR.md ga bu darsning yangi xabarlarini qo'shing: TS2741, TS2430, TS2320, TS2312, TS2300, TS2717, TS2440 va "never ga berib bo'lmaydi" holati. Keyin kurs/mashqlar papkasidan:
git add 15/16-kengaytirish 15/XATOLAR.md
git commit -m "15/16: menu.ts — extends, kesishma, augmentation"9. Real ishda
- API javoblari:
type ApiUser = User & { createdAt: string }— serverdan kelgan obyekt o'z turi va qo'shimcha xizmat maydonlari bilan.&bilan "asosiy tur + metama'lumot" juda keng tarqalgan. - Komponent xususiyatlari: React'da (interfeys kutubxonasi, 17-qismdan)
type ButtonProps = BaseProps & { onClick: () => void }— har qadamda uchraydi. - Kutubxona turlarini kengaytirish:
declare global { interface Window { … } }(analitika skripti qo'shgan global),declare module "express"(so'rovgauser), muhit o'zgaruvchilari turi — hammasi declaration merging. - Intervyu: "
interfacevatypefarqi?" savolining kutilgan javobi aynan shu dars:extendsva&, declaration merging faqatinterfaceda, ziddiyat qachon ko'rinadi.
Xulosa
interface B extends A— A ning hamma maydoni va yana B niki. Bir nechta ota ham bo'ladi:extends A, C. Ziddiyat — darholTS2430/TS2320.A & B— kesishma: "ham A, ham B".typelar uchun asosiy birlashtirish usuli, union bilan ham ishlaydi, nom bermasdan ham yoziladi.- Kesishmadagi ziddiyat jim: maydon (yoki butun tur)
neverbo'ladi, xato faqat ishlatishda chiqadi. - Bir nomli
interfacelar birlashadi (declaration merging),type—TS2300. Global turgadeclare global, modul turigadeclare module "…"bilan qo'shiladi. declare— faqat va'da: qiymat yaratmaydi va ishlash paytida tekshirilmaydi.
Keyingi dars: Index signature va Record — kalitlari oldindan noma'lum obyektlarni (masalan, "taom nomi → narx" lug'atini) tiplaymiz va undagi yashirin undefined ni ko'ramiz.
Manbalar
- TypeScript Handbook: "Object Types" (Extending Types, Intersection Types) — typescriptlang.org/docs/handbook/2/objects.html#extending-types
- TypeScript Handbook: "Declaration Merging" (Merging Interfaces, Module Augmentation, Global augmentation) — typescriptlang.org/docs/handbook/declaration-merging.html
- TypeScript Wiki: "Performance" (Preferring Interfaces Over Intersections) — github.com/microsoft/TypeScript/wiki/Performance
- TypeScript Handbook: "Everyday Types" (Differences Between Type Aliases and Interfaces) — typescriptlang.org/docs/handbook/2/everyday-types.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!