Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. type: turga nom berish
- 2.1 Birinchi tur taxallusi
- 2.2 Xabarlarda endi nom ko'rinadi
- 2.3 type — istalgan turga
- 2.4 Taxallus — yangi tur emas
- 2.5 Tur — faqat tekshiruv uchun
- 3. interface: obyekt shakli uchun
- 3.1 Yozilishi
- 3.2 interface — faqat obyekt shakli
- 3.3 Kengaytirish: extends
- 3.4 Declaration merging: bir xil nomli interfeyslar birlashadi
- 4. Farqlari — bitta jadvalda
- 4.1 Yashirin farq: index signature
- 5. Qaysi birini tanlash?
- 6. TypeScript nimani ushlamaydi
- 7. Ko'p uchraydigan xatolar
- 7.1 interface dan keyin =
- 7.2 Tur nomini qiymat sifatida ishlatish
- 7.3 Taxallusni himoya deb o'ylash
- 7.4 Ma'lumot uchun interface va Record tuzog'i
- 8. Mashqlar
- 1-mashq (oson): type yoki interface?
- 2-mashq (o'rta): Chekni nomli turlar bilan qayta yozing
- 3-mashq (qiyin): Record xatosini tushuntiring va tuzating
- 4-mashq: Vazifalar qadami — turlar.ts
- 9. Real ishda
- Xulosa
- Manbalar
TypeScript type va interface: turga nom berish va qaysi birini tanlash
Qisqacha:
typevainterface— ikkalasi ham turga nom beradi:type MenuItem = { name: string; price: number }yokiinterface MenuItem { name: string; price: number }. Obyekt shakli uchun ular deyarli bir xil ishlaydi. Farqi:typeistalgan turga nom bera oladi (union, tuple, son),interfaceesa faqat obyekt shakliga, lekin u bir xil nomli e'lonlarni birlashtiradi. Kursdagi qoida: ma'lumot shakllari —type, birlashtirish yoki kengaytirish kerak bo'lsa —interface.
Bu darsda
typebilan istalgan turga nom berasiz va uni ko'p joyda ishlatasiz.interfaceyozasiz vatypedan nimasi bilan farq qilishini bilasiz.- Declaration merging — bir xil nomli
interfacelar birlashishini ko'rasiz. interfacening yashirin farqini — index signature tuzog'ini — haqiqiy loyiha xatosida tushunasiz.vazifalarning birinchi.tsfaylini —turlar.tsni yaratasiz.
Oldin bilishingiz kerak: Obyekt turlari, Massiv va tuple turlari, JSDoc va // @ts-check.
1. Nega bu kerak?
O'tgan darsning oxirgi mashqida addLine funksiyasi shunday bo'lib qoldi:
export function addLine(
order: { readonly guest: string; readonly lines: readonly Line[] },
line: Line,
): { readonly guest: string; readonly lines: readonly Line[] } {
return { ...order, lines: [...order.lines, line] };
}Buyurtmaning shakli ikki marta yozilgan: parametrda va natijada. receiptLines da — uchinchi marta, biroz boshqacha. Ertaga Jasur aka buyurtmaga stol raqamini qo'shishni so'raydi. Sardor table: number ni bir joyga qo'shadi, ikkinchisini unutadi — va turlar bir-biridan ajralib ketadi.
JavaScript'da takrorlanadigan kodni funksiyaga chiqaramiz va unga nom beramiz. Turlarda ham xuddi shunday: takrorlanadigan turni bir marta yozib, unga nom beramiz. Buning ikki yo'li bor — type va interface.
2. type: turga nom berish
2.1 Birinchi tur taxallusi
type MenuItem = {
name: string;
price: number;
};
const osh: MenuItem = { name: "Osh", price: 35000 };
const manti: MenuItem = { name: "Manti", price: 30000 };
console.log(osh.price + manti.price); // 65000type MenuItem = { … } — "bundan buyon MenuItem deganim shu obyekt turi". Bu tur taxallusi (type alias). Taxallus — boshqa nom: telefoningizdagi kontaktlar kabi. +998 90 000 00 00 ni har safar yodda saqlamaysiz, "Onam" deb yozib qo'yasiz. Raqam o'zgarmaydi, unga qulay nom berilgan xolos.
Yozilish qoidalari:
typeso'zi, nom,=, keyin istalgan tur va oxirida;.- Nom katta harf bilan boshlanadi (PascalCase):
MenuItem,OrderLine. O'zgaruvchilar kichik harf bilan (osh), turlar katta harf bilan — kodda ularni darhol ajratasiz.
2.2 Xabarlarda endi nom ko'rinadi
O'tgan darsda xato xabarlari uzun turlarni to'liq ko'rsatardi. Nom bo'lsa — qisqaroq:
type MenuItem = {
name: string;
price: number;
};
const lagmon: MenuItem = { name: "Lag'mon" };Kompilyator:
TS2741: Property 'price' is missing in type '{ name: string; }' but required in type 'MenuItem'.Xabar endi { name: string; price: number; } emas, MenuItem deydi. Kod va xato bir tilda gapiradi.
2.3 type — istalgan turga
type faqat obyektlar uchun emas. = dan keyin istalgan tur tura oladi:
type Price = number;
type Range = [open: number, close: number];
type Size = "kichik" | "o'rta" | "katta";
type Menu = readonly MenuItem[];
type MenuItem = { name: string; price: number };
const hours: Range = [7, 23];
const size: Size = "katta";
const menu: Menu = [{ name: "Osh", price: 35000 }];
const tea: Price = 5000;
console.log(hours, size, menu.length, tea); // [ 7, 23 ] katta 1 5000Range— o'tgan darslardagi nomlangan tuple.Size— uchta satrdan biri.|bilan yozilgan bu tur — union (birlashma); uni Union va literal turlar darsida to'liq o'rganamiz.Menu— boshqa nomli turdan yasalgan tur.MenuItempastda e'lon qilingan bo'lsa ham ishladi: turlar tartibga bog'liq emas.
2.4 Taxallus — yangi tur emas
Muhim nozik joy. type Price = number yangi tur yaratmaydi — u faqat number ning ikkinchi nomi:
type Price = number;
type Percent = number;
function withTip(amount: Price, tip: Percent): number {
return amount + (amount * tip) / 100;
}
const bill: Price = 70000;
const tip: Percent = 10;
console.log(withTip(tip, bill)); // 7010Argumentlar almashib ketdi — withTip(10, 70000): 10 so'mlik hisobga 70 000 % choychaqa. tsc jim, chunki Price ham, Percent ham aslida number. Kontaktdagi "Onam" va "Opam" bitta raqamga yozilgan bo'lsa, telefon ularni farqlamaydi. Bunday turlarni haqiqatan ajratadigan usul bor — Branded types darsida. Hozircha eslab qoling: taxallus o'qishni osonlashtiradi, lekin himoya qo'shmaydi.
2.5 Tur — faqat tekshiruv uchun
Tur nomini qiymat sifatida ishlatib bo'lmaydi:
type MenuItem = { name: string; price: number };
console.log(MenuItem);Kompilyator:
TS2693: 'MenuItem' only refers to a type, but is being used as a value here.Bu xabarni o'tgan darsda string bilan ko'rgan edik. tsc .js yozganda type … qatorlari butunlay o'chadi — ishlash paytida MenuItem degan narsa yo'q (Nega TypeScript darsidagi "turlar o'chadi" qoidasi).
Tekshirib ko'ring:
type Phone = string;vaconst p: Phone = "+998 90 000 00 00";— keyinconst s: string = p;xato beradimi?
Javob
Xato yo'q. Phone — string ning boshqa nomi, ular to'liq bir xil tur. Taxallus "faqat telefon raqami" degan himoya qo'shmaydi: const p: Phone = "salom" ham xatosiz o'tadi.
3. interface: obyekt shakli uchun
3.1 Yozilishi
interface — obyekt turiga nom berishning ikkinchi yo'li:
interface MenuItem {
name: string;
price: number;
}
const somsa: MenuItem = { name: "Somsa", price: 8000 };
console.log(`${somsa.name}: ${somsa.price}`); // Somsa: 8000type dan farqi yozuvda: = yo'q, jingalak qavs to'g'ridan-to'g'ri nomdan keyin keladi va oxirida ; shart emas. Interfeys (interface) so'zi "ikki tomon orasidagi kelishilgan chegara" degani: rozetka va vilka kabi — shakli mos kelsa, ulanadi.
Ishlatilishi type bilan bir xil: o'zgaruvchi, parametr, natija turi. Xabarlar ham nom bilan chiqadi.
3.2 interface — faqat obyekt shakli
interface ga union yoki son yozib bo'lmaydi:
interface Size = "kichik" | "katta";Kompilyator:
TS1005: '{' expected.Tarjimasi: "{ kutilgan edi". Bu tur xatosi emas, sintaksis xatosi: interface dan keyin nom va darhol { kelishi shart. Union, tuple, oddiy tur — faqat type bilan.
3.3 Kengaytirish: extends
interface boshqa interfeysning hamma xususiyatlarini olib, ustiga qo'shishi mumkin:
interface MenuItem {
name: string;
price: number;
}
interface Dish extends MenuItem {
cookMinutes: number;
}
const plov: Dish = { name: "Osh", price: 35000, cookMinutes: 40 };
console.log(plov.name, plov.cookMinutes); // Osh 40extends — klasslardagi merosga o'xshash so'z: Dish da MenuItem ning hamma xususiyati bor, ustiga cookMinutes. type da bu ish & belgisi bilan qilinadi. Ikkalasini ham, ularning tuzoqlarini ham Kengaytirish, kesishma va declaration merging darsida batafsil ko'ramiz.
3.4 Declaration merging: bir xil nomli interfeyslar birlashadi
Bu interface ning o'ziga xos xususiyati. Bir nomli ikkita interface yozsangiz, ular xato bermaydi — birlashadi:
interface Guest {
name: string;
}
interface Guest {
phone: string;
}
const dilshod: Guest = { name: "Dilshod aka" };Kompilyator:
TS2741: Property 'phone' is missing in type '{ name: string; }' but required in type 'Guest'.TypeScript aytyapti: «{ name: string; } turida phone yo'q, Guest turida esa u majburiy». Biz phone ni birinchi e'longa yozmagan edik — u ikkinchisidan keldi.
Guest endi ikkala e'londagi xususiyatlarga ega: name va phone. Bu declaration merging (e'lonlarni birlashtirish). type bilan esa bu ishlamaydi:
type Guest = { name: string };
type Guest = { phone: string };Kompilyator:
TS2300: Duplicate identifier 'Guest'.
TS2300: Duplicate identifier 'Guest'."Takroriy nom Guest" — har ikki e'lon uchun bittadan xabar. Taxallus bitta bo'ladi.
Birlashtirish nimaga kerak? Asosan boshqalarning turlarini to'ldirish uchun: brauzerning Window turiga o'z xususiyatingizni qo'shish yoki kutubxona turini kengaytirish. O'z kodingizda tasodifiy birlashish esa xavfli: ikki faylda bir xil nomli interface yozib qo'ysangiz, ular jim qo'shilib ketadi. Bu mavzu ham Kengaytirish va declaration merging darsida.
Tekshirib ko'ring:
type Range = [number, number];niinterfacebilan yozib bo'ladimi?
Javob
Yo'q. Tuple — massiv turi, interface esa faqat { … } shaklidagi obyekt turiga nom beradi. Tuple, union ("kichik" | "katta") va oddiy tur (number) uchun faqat type ishlaydi.
4. Farqlari — bitta jadvalda
| Imkoniyat | type |
interface |
|---|---|---|
| Obyekt shakli | ||
| Union, tuple, oddiy tur | ||
| Kengaytirish | & (kesishma) |
extends |
| Bir nom bilan ikki marta | TS2300 |
birlashadi |
Record<string, unknown> ga mos |
(pastda) |
Oxirgi qator — eng kam ma'lum va eng ko'p tuzoqqa tushiradigan farq. Uni alohida ko'ramiz.
4.1 Yashirin farq: index signature
JSDoc darsida Record<string, unknown> ni ko'rgan edik — "kalitlari satr, qiymatlari hali noma'lum obyekt". Har qanday oddiy obyekt shunday ko'rinadi. Masalan, obyekt xususiyatlarini chiqaradigan yordamchi funksiya:
interface MenuItem {
name: string;
price: number;
}
function printFields(obj: Record<string, unknown>) {
console.log(Object.keys(obj).join(", "));
}
const osh: MenuItem = { name: "Osh", price: 35000 };
printFields(osh);Kompilyator:
TS2345: Argument of type 'MenuItem' is not assignable to parameter of type 'Record<string, unknown>'.
Index signature for type 'string' is missing in type 'MenuItem'.Tarjimasi: "MenuItem turidagi argumentni Record<string, unknown> parametriga berib bo'lmaydi. MenuItem turida string uchun index signature yo'q". Index signature — "istalgan satr kalit bo'yicha murojaat qilish" qoidasi (massivlarda uni raqamli indeks uchun ko'rgan edik).
Endi o'sha kodni type bilan:
type MenuItem = {
name: string;
price: number;
};
function printFields(obj: Record<string, unknown>) {
console.log(Object.keys(obj).join(", "));
}
const osh: MenuItem = { name: "Osh", price: 35000 };
printFields(osh); // name, priceXato yo'q. Nega farq? Sababi — declaration merging. interface MenuItem ga istalgan paytda, istalgan faylda yangi xususiyat qo'shilishi mumkin. Ehtimol, unda kalitlari satr bo'lmagan narsa ham paydo bo'ladi. Shuning uchun TypeScript interface ga "hamma kalitlarim satr" degan yashirin va'dani bermaydi. type esa yopiq: u bir marta e'lon qilinadi va shakli to'liq ma'lum. TypeScript unga yashirin index signature beradi.
Bu nazariy farq emas. Aynan shu xato vazifalar loyihasida haqiqatan chiqdi — «Vazifalar qadami» mashqida ko'rasiz.
5. Qaysi birini tanlash?
TypeScript hujjatlari (Handbook) shunday deydi: obyekt shakllari uchun ko'pincha farqi yo'q, didga qarab tanlang; agar qoida kerak bo'lsa — interface dan boshlang, type imkoniyati kerak bo'lganda o'ting. Ko'p jamoalar esa teskarisini tanlaydi: hamma joyda type. Ikkalasi ham to'g'ri — muhimi, bitta loyihada bitta qoida.
Bu kursda va vazifalar da qoida shunday:
flowchart TD
A["Turga nom kerak"] --> B{"Obyekt shaklimi?"}
B -- "yo'q: union, tuple, son" --> T["type"]
B -- "ha" --> C{"Boshqa turni kengaytirish<br/>yoki birlashtirish kerakmi?"}
C -- "ha: Window, kutubxona,<br/>klass shartnomasi" --> I["interface"]
C -- "yo'q: oddiy ma'lumot" --> T2["type"]| Holat | Tanlov | Nega |
|---|---|---|
| JSON, API javobi, holat obyekti | type |
Record<string, unknown> bilan ishlaydi, yopiq |
| Union, tuple, literal | type |
interface qila olmaydi |
| Brauzer yoki kutubxona turini to'ldirish | interface |
faqat u birlashadi |
| Klass bajaradigan shartnoma | interface |
odatiy uslub (Klasslar) |
Katta loyihalarda yana bir sabab tilga olinadi: ko'p darajali extends zanjirini tsc & dan biroz tezroq tekshiradi (TypeScript wiki, "Performance"). Bizning hajmda bu sezilmaydi.
Tekshirib ko'ring: Server javobi uchun
type Answer = { ok: boolean; items: string[] }yozdingiz. Hamkasbingiz "interface ga o'tkazaylik" dedi. Bu javobRecord<string, unknown>kutadigan tekshiruv funksiyasiga beriladi. Nima deysiz?
Javob
type qolsin. interface Answer bo'lsa, Record<string, unknown> parametriga berishda TS2345 ("Index signature for type 'string' is missing") chiqadi. Server javobi — oddiy ma'lumot, uni kengaytirish yoki birlashtirish kerak emas: kurs qoidasi bo'yicha bu type.
6. TypeScript nimani ushlamaydi
Ikki narsani eslab qoling.
Taxallus himoya qilmaydi. type OrderId = number va type TableId = number — bir xil tur. cancelOrder(tableId) deb noto'g'ri raqam bersangiz, tsc jim («Taxallus — yangi tur emas» bo'limidagi choychaqa misoli).
Birlashish jim bo'ladi. Ikki faylda bir xil nomli global interface bo'lsa, ular xatosiz qo'shiladi. Natijada tur siz kutmagan xususiyatlarni talab qiladi. Xato boshqa joyda, tushunarsiz is missing bilan chiqadi.
interface Settings {
currency: string;
}
// ... boshqa joyda, boshqa dasturchi:
interface Settings {
rounding?: { step: number };
}
const settings: Settings = { currency: "so'm" };
console.log(settings.rounding?.step ?? 1000); // 1000
const raw: Settings = JSON.parse('{"currency":"so\'m","rounding":5}');
console.log(raw.rounding?.step.toFixed(0));Konsolda:
1000
TypeError: Cannot read properties of undefined (reading 'toFixed')tsc — xatosiz: Settings birlashdi, rounding — ixtiyoriy obyekt. JSON'da esa rounding — son (5), uning step i yo'q. Bu yerda ikki muammo birga: birlashish turni jim o'zgartirdi, JSON.parse esa uni tekshirmasdan qabul qildi (Obyekt turlari darsidagi tuzoq).
7. Ko'p uchraydigan xatolar
7.1 interface dan keyin =
interface MenuItem = { … } — TS1005: '{' expected. Tuzatish: interface MenuItem { … } (tenglik belgisisiz) yoki type MenuItem = { … }.
7.2 Tur nomini qiymat sifatida ishlatish
console.log(MenuItem), obj instanceof MenuItem — TS2693. Tur ishlash paytida yo'q. Tuzatish: qiymatni tekshirish kerak bo'lsa, xususiyatlarni tekshiring (Custom type guard darsida).
7.3 Taxallusni himoya deb o'ylash
type Price = number — number ning o'zi. Tuzatish: argumentlar adashishi mumkin bo'lsa, obyekt parametr ishlating: withTip({ amount, tip }) — nomlar adashtirmaydi.
7.4 Ma'lumot uchun interface va Record tuzog'i
TS2345 … Index signature for type 'string' is missing. Tuzatish: ma'lumot shakli uchun type ishlating.
8. Mashqlar
1-mashq (oson): type yoki interface?
Har holat uchun kurs qoidasi bo'yicha tanlang.
Buyurtma holati — "yangi" | "tayyor" | "berildi": [:type]. Brauzerdagi Window turiga appVersion qo'shish: [:interface]. Bir nomli ikki e'lon qaysi birida birlashadi: .
Yechim
- Holat — union, uni faqat
typeifodalaydi. Window— brauzerning tayyorinterfacei; unga xususiyat qo'shish — declaration merging, demakinterface.- Birlashish — faqat
interfaceda;typeikki marta e'lon qilinsaTS2300beradi.
2-mashq (o'rta): Chekni nomli turlar bilan qayta yozing
O'tgan darsdagi receipt.ts da buyurtma shakli uch joyda takrorlangan edi. Line va Order turlarini e'lon qiling (note — ixtiyoriy) va funksiyalarni ular bilan yozing. Natija va xulq o'zgarmasin.
Yechim
type Line = {
readonly dish: string;
readonly price: number;
readonly qty: number;
};
type Order = {
readonly guest: string;
readonly lines: readonly Line[];
readonly note?: string;
};
function lineTotal(line: Line): number {
return line.price * line.qty;
}
function addLine(order: Order, line: Line): Order {
return { ...order, lines: [...order.lines, line] };
}
const first: Order = {
guest: "Malika",
lines: [{ dish: "Osh", price: 35000, qty: 1 }],
};
const tea: Line = { dish: "Ko'k choy", price: 5000, qty: 2 };
const next = addLine(first, tea);
console.log(next.lines.map(lineTotal)); // [ 35000, 10000 ]
console.log(first.lines.length); // 1addLine imzosi uch qatordan bitta qatorga tushdi: (order: Order, line: Line): Order. Ertaga table xususiyati qo'shilsa — faqat Order ga yoziladi. addLine ni chaqirish uchun buyurtmada guest majburiy bo'lib qoldi. O'tgan darsdagi receiptLines ga esa guest siz obyekt berilardi — u ham Order ni olsa, testdagi chaqiruvlarga guest qo'shish kerak bo'ladi. Turga nom berish shunday qarorlarni ochiq qiladi.
3-mashq (qiyin): Record xatosini tushuntiring va tuzating
Kod tsc dan o'tmaydi:
interface Booking {
guest: string;
people: number;
}
function hasEmptyField(obj: Record<string, unknown>): boolean {
return Object.values(obj).some((v) => v === "" || v === undefined);
}
const b: Booking = { guest: "", people: 4 };
console.log(hasEmptyField(b));Kompilyator:
TS2345: Argument of type 'Booking' is not assignable to parameter of type 'Record<string, unknown>'.
Index signature for type 'string' is missing in type 'Booking'.(1) Xabarning ikkinchi qatorini o'z so'zlaringiz bilan tushuntiring. (2) Kurs qoidasi bo'yicha tuzating. (3) Booking ni kengaytirib, table: number qo'shilgan TableBooking turini e'lon qiling va uni ham hasEmptyField ga bering.
Yechim
(1) Record<string, unknown> — "har qanday satr kalit bo'yicha o'qish mumkin bo'lgan obyekt". Endi Booking ga qarang. interface Booking ga keyinroq boshqa joyda yangi xususiyatlar qo'shilishi mumkin (declaration merging), shuning uchun TypeScript unga bunday va'da bermaydi. type esa yopiq — unga beradi.
(2) va (3):
type Booking = {
guest: string;
people: number;
};
type TableBooking = Booking & { table: number };
function hasEmptyField(obj: Record<string, unknown>): boolean {
return Object.values(obj).some((v) => v === "" || v === undefined);
}
const b: Booking = { guest: "", people: 4 };
const t: TableBooking = { guest: "Bobur", people: 2, table: 5 };
console.log(hasEmptyField(b), hasEmptyField(t)); // true falseBooking & { table: number } — "Booking ning hamma xususiyati va table". type da kengaytirish shunday yoziladi (& — kesishma, batafsil — keyinroq). Ikkala tur ham Record<string, unknown> ga mos: b da mehmon ismi bo'sh — true.
4-mashq: Vazifalar qadami — turlar.ts
vazifalar ning eng ko'p ishlatiladigan turi — VazifaMalumoti (id, matn, bajarildi). 12/39 dan beri u vazifa.js da JSDoc @typedef bilan yozilgan, boshqa modullar uni @import bilan oladi. Bu qadamda ilovaning birinchi .ts fayli paydo bo'ladi: assets/js/turlar.ts — faqat turlar. JavaScript modullar avvalgidek .js bo'lib qoladi va turlarni yangi fayldan oladi.
- Branch:
refactor/turlar - Commit:
refactor: turlar.ts — VazifaMalumoti va Paket; typescript-eslint, tanasi:JSDoc @typedef o'rniga import; eslint .ts uchun
assets/js/turlar.ts(yangi):VazifaMalumotiva eksport fayli shakliPaket(versiya— aniq1,vazifalar— o'zgarmas massiv). Hamma xususiyatlarreadonly.vazifa.jsdan@typedefolib tashlanadi.vazifa.js,royxat.js,api.js,paket.js—/** @import { … } from "./turlar.ts" */.tsconfig.jsonga ikki bayroq:erasableSyntaxOnly,verbatimModuleSyntax.- ESLint
.tsfayllarni ham tekshirsin: typescript-eslint plagini va TypeScript'ni o'rnatish darsida aytilgan ikki TypeScript paketi.
Tekshiruv: npm run build, npm test, npm run tip, npm run lint, npm run format:check — hammasi exit 0.
Yechim
Avval — interface bilan. Darsning «Qaysi birini tanlash?» bo'limidagi Handbook maslahatiga ko'ra birinchi variant interface bilan yozildi:
// turlar.ts — birinchi urinish
export interface VazifaMalumoti {
readonly id: number;
readonly matn: string;
readonly bajarildi: boolean;
}npm run tip darhol to'xtadi:
assets/js/paket.js(85,36): error TS2677: A type predicate's type must be assignable to its parameter's type.
Type 'VazifaMalumoti[]' is not assignable to type 'Record<string, unknown>[]'.
Type 'VazifaMalumoti' is not assignable to type 'Record<string, unknown>'.
Index signature for type 'string' is missing in type 'VazifaMalumoti'.Xato paket.js dagi malumotlarniTekshir da — 12/39 dagi assertion funksiya: @returns {asserts malumotlar is VazifaMalumoti[]}. U Record<string, unknown>[] ni ("hali tekshirilmagan obyektlar") VazifaMalumoti[] ga toraytiradi. Toraytirish faqat mos turga mumkin: birinchi qator shuni aytadi ("tur predikatining turi parametr turiga mos bo'lishi kerak"). Zanjirning eng pastki qatori esa — «Yashirin farq: index signature» bo'limidagi sabab. interface ga yashirin index signature berilmaydi.
Keyin — type bilan. Xato 0. Kanonning qoidasi shu xatodan chiqdi: turlar.ts dagi ma'lumot shakllari — type.
// turlar.ts — ilovaning ma'lumot shakllari. Faqat turlar: build'dan
// keyin bo'sh modul, brauzer uni yuklamaydi (import type).
// interface emas, type: JSON'dan kelgan oddiy obyekt Record<string,
// unknown> ga mos kelishi kerak (paket.js tekshiruvi) — interface'ga
// yashirin index signature berilmaydi, type alias'ga beriladi
/** Vazifaning JSON'dagi shakli: eksport, kesh va server shu ko'rinishda */
export type VazifaMalumoti = {
readonly id: number;
readonly matn: string;
readonly bajarildi: boolean;
};
/**
* Eksport fayli va kesh (`localStorage`) matni. Shakl o'zgarsa —
* versiya oshadi, eski matnlar rad etiladi
*/
export type Paket = {
readonly versiya: 1;
readonly vazifalar: readonly VazifaMalumoti[];
};export type— tur boshqa fayllarga eksport qilinadi, xuddi funksiya kabi.versiya: 1— literal tur: faqat1soni (Tur annotatsiyasi darsidagi literal turlar).readonlyhamma joyda — o'qilgan ma'lumotni ilova o'zgartirmaydi,Vazifaklassi o'z nusxasini yasaydi.
JS modullar turni yangi fayldan oladi. vazifa.js boshida @typedef (7 qator) o'rniga bitta qator:
// @ts-check
// vazifa.js — bitta vazifa va vazifalar xatosi
/** @import { VazifaMalumoti } from "./turlar.ts" */royxat.js va api.js da faqat yo'l o'zgardi: "./vazifa.js" → "./turlar.ts". paket.js da yana bir qiziq qator:
/** @import { Paket, VazifaMalumoti } from "./turlar.ts" */
// Eksport shakli o'zgarsa — versiya oshadi, eski matnlar rad etiladi.
// Turi — 1 (literal), number emas: Paket bilan bir joyda o'zgaradi
/** @type {Paket["versiya"]} */
const PAKET_VERSIYASI = 1;Paket["versiya"] — "Paket turining versiya xususiyatining turi", ya'ni 1. Ertaga versiya 2 bo'lsa, faqat turlar.ts o'zgaradi, tsc esa PAKET_VERSIYASI = 1 qatorini darhol xato deydi. Bu yozuv (indexed access) — keyof, typeof va indexed access darsida.
tsconfig.json — ikki yangi bayroq:
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,erasableSyntaxOnly("faqat o'chiriladigan sintaksis") — testlar.tsfayllarni Node 24 da to'g'ridan-to'g'ri ishga tushiradi. Node esa turlarni shunchaki o'chiradi (TypeScript'ni o'rnatish darsidagi type stripping). O'chirib bo'lmaydigan, JavaScript kodi yasaydigan TypeScript imkoniyatlari (enumva boshqalar) taqiqlanadi. Ular haqida —enumva uning muqobillari darsida.verbatimModuleSyntax— faqat turni import qilsangiz, buni aniq yozasiz:import type { … }. Build'da bunday import butunlay o'chadi. Uni keyingi qadamda birinchi.tsmodulda ishlatamiz.
ESLint .ts uchun. O'rnatish — bitta buyruq:
npm install --save-dev --save-exact \
"@typescript/native@npm:typescript@7.0.2" \
"typescript@npm:@typescript/typescript6@6.0.2" \
typescript-eslint@8.71.1package.json da shunday bo'ladi:
"@typescript/native": "npm:typescript@7.0.2",
"typescript": "npm:@typescript/typescript6@6.0.2",
"typescript-eslint": "8.71.1",Nega ikki TypeScript? typescript-eslint 8.71.1 kompilyatorning dasturiy API'si bilan ishlaydi va typescript versiyasini >=4.8.4 <6.1.0 deb talab qiladi. TypeScript 7.0 da esa API hali yo'q (7.1 da va'da qilingan). Shuning uchun TS 7.0 e'lonida taklif qilingan yo'l: typescript nomi ostida TS 6 (faqat ESLint uchun), @typescript/native nomi ostida TS 7. npx tsc --version → Version 7.0.2, npx tsc6 --version → Version 6.0.3. Holat 2026-yil oktabr uchun: 7.1 chiqqach, yana bitta paket qoladi.
eslint.config.js ga yangi blok:
import tseslint from "typescript-eslint";
// ...
{
// .ts fayllar: typescript-eslint tahlilchisi (parser) va qoidalari.
// Turlarni tekshirish — tsc ishi (npm run tip), bu yer — kod uslubi
// va xatoga boshlaydigan joylar (any, ishlatilmagan o'zgaruvchi...)
files: ["**/*.ts"],
extends: [tseslint.configs.recommended],
},Brauzer globallari va XSS taqiqlari (13-qismdagi qoidalar) endi assets/js/**/*.{js,ts} ga — .ts fayllarga ham — qo'llanadi. Vaqtinchalik .ts faylda sinab ko'rdik: x: any — @typescript-eslint/no-explicit-any, ishlatilmagan o'zgaruvchi — @typescript-eslint/no-unused-vars, el.innerHTML = … — o'sha XSS xabari. Hammasi ushlandi.
Tekshiruv. npm test — 137/137 (testlar o'zgarmadi). tip, lint, format:check — toza. npm run build dan keyin dist/assets/js/turlar.js da bitta qator qoladi:
export {};Turlar o'chdi, fayl bo'sh modulga aylandi. Brauzer uni so'ramaydi ham: sw.js dagi QOBIQ ro'yxatida ham, index.html dagi modulepreload da ham turlar.js yo'q. 159 ta brauzer tekshiruvi dist/ da — 159/159. Ilova xulqi o'zgarmadi.
Commit va PR:
git switch -c refactor/turlar
# turlar.ts, @import'lar, tsconfig.json, eslint.config.js, paketlar
npm run build && npm test && npm run tip
npm run lint && npm run format:check
git add .
git commit -m "refactor: turlar.ts — VazifaMalumoti va Paket; typescript-eslint" \
-m "JSDoc @typedef o'rniga import; eslint .ts uchun"
git push -u origin refactor/turlar
gh pr create --fill
gh pr merge --mergeSarlavha — 66 belgi. Diff: 9 fayl, +436 −43 (ko'pi — package-lock.json dagi yangi paketlar).
9. Real ishda
- Katta loyihalarda turlar ko'pincha alohida faylda yoki papkada turadi:
types.ts,types/—vazifalardagiturlar.tskabi. Komponentlar va funksiyalar ularniimport typebilan oladi. - Kutubxonalar (React, Express) ommaviy turlarini ko'pincha
interfacebilan yozadi — aynan siz ularni declaration merging bilan to'ldira olishingiz uchun. - Jamoa uslubi —
typeyokiinterfacetanlovi odatda ESLint qoidasi bilan mustahkamlanadi: typescript-eslint'daconsistent-type-definitionsqoidasi bor. - Intervyu: "
typevainterfacefarqi?" — junior TypeScript intervyusidagi eng mashhur savollardan. Javobda declaration merging, union va index signature farqini aytsangiz — kuchli javob.
Xulosa
type Nom = …— istalgan turga taxallus: obyekt, union, tuple, oddiy tur. Taxallus yangi tur yaratmaydi.interface Nom { … }— faqat obyekt shakli.extendsbilan kengayadi, bir xil nomli e'lonlar birlashadi (declaration merging).interfacega yashirin index signature berilmaydi:Record<string, unknown>ga mos emas (TS2345,TS2677).typega beriladi.- Kurs qoidasi: ma'lumot shakllari —
type; kengaytirish yoki birlashtirish kerak bo'lsa —interface. Bitta loyihada — bitta qoida. - Turlar faqat tekshiruvda yashaydi:
turlar.tsbuild'dan keyinexport {};ga aylanadi.
Keyingi dars: Union va literal turlar — qiymat bir nechta aniq variantdan biri bo'lishi mumkin: "hammasi" | "faol" | "bajarilgan", va TypeScript buni qat'iy tekshiradi.
Manbalar
- TypeScript Handbook: "Everyday Types" → Type Aliases, Interfaces, Differences Between Type Aliases and Interfaces — typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases
- TypeScript Handbook: "Declaration Merging" — typescriptlang.org/docs/handbook/declaration-merging.html
- TypeScript wiki: "Performance" → Preferring Interfaces Over Intersections — github.com/microsoft/TypeScript/wiki/Performance
- TypeScript 7.0 e'loni: "Running side-by-side with TypeScript 6.0" — devblogs.microsoft.com/typescript/announcing-typescript-7-0/
- typescript-eslint: Getting Started — typescript-eslint.io/getting-started
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!