IlmHamroh
JavaScript Full-stack/15-qism. TypeScript6/47-dars21 daqiqa
Mundarija (30)

TypeScript type va interface: turga nom berish va qaysi birini tanlash

Qisqacha: type va interface — ikkalasi ham turga nom beradi: type MenuItem = { name: string; price: number } yoki interface MenuItem { name: string; price: number }. Obyekt shakli uchun ular deyarli bir xil ishlaydi. Farqi: type istalgan turga nom bera oladi (union, tuple, son), interface esa 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

  • type bilan istalgan turga nom berasiz va uni ko'p joyda ishlatasiz.
  • interface yozasiz va type dan nimasi bilan farq qilishini bilasiz.
  • Declaration merging — bir xil nomli interface lar birlashishini ko'rasiz.
  • interface ning yashirin farqini — index signature tuzog'ini — haqiqiy loyiha xatosida tushunasiz.
  • vazifalar ning birinchi .ts faylini — turlar.ts ni 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:

ts
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

ts
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); // 65000

type 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:

  • type so'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:

ts
type MenuItem = {
  name: string;
  price: number;
};

const lagmon: MenuItem = { name: "Lag'mon" };

Kompilyator:

text
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:

ts
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 5000
  • Range — 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. MenuItem pastda 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:

ts
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)); // 7010

Argumentlar 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:

ts
type MenuItem = { name: string; price: number };
console.log(MenuItem);

Kompilyator:

text
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; va const p: Phone = "+998 90 000 00 00"; — keyin const 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:

ts
interface MenuItem {
  name: string;
  price: number;
}

const somsa: MenuItem = { name: "Somsa", price: 8000 };
console.log(`${somsa.name}: ${somsa.price}`); // Somsa: 8000

type 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:

ts
interface Size = "kichik" | "katta";

Kompilyator:

text
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:

ts
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 40

extends — 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:

ts
interface Guest {
  name: string;
}
interface Guest {
  phone: string;
}

const dilshod: Guest = { name: "Dilshod aka" };

Kompilyator:

text
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:

ts
type Guest = { name: string };
type Guest = { phone: string };

Kompilyator:

text
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]; ni interface bilan 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:

ts
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:

text
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:

ts
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, price

Xato 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 javob Record<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.

ts
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:

text
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 type ifodalaydi.
  • Window — brauzerning tayyor interface i; unga xususiyat qo'shish — declaration merging, demak interface.
  • Birlashish — faqat interface da; type ikki marta e'lon qilinsa TS2300 beradi.

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
ts
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); // 1

addLine 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:

ts
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:

text
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):

ts
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 false

Booking & { 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
  1. assets/js/turlar.ts (yangi): VazifaMalumoti va eksport fayli shakli Paket (versiya — aniq 1, vazifalar — o'zgarmas massiv). Hamma xususiyatlar readonly.
  2. vazifa.js dan @typedef olib tashlanadi. vazifa.js, royxat.js, api.js, paket.js — /** @import { … } from "./turlar.ts" */.
  3. tsconfig.json ga ikki bayroq: erasableSyntaxOnly, verbatimModuleSyntax.
  4. ESLint .ts fayllarni 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:

ts
// turlar.ts — birinchi urinish
export interface VazifaMalumoti {
  readonly id: number;
  readonly matn: string;
  readonly bajarildi: boolean;
}

npm run tip darhol to'xtadi:

text
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.

ts
// 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: faqat 1 soni (Tur annotatsiyasi darsidagi literal turlar).
  • readonly hamma joyda — o'qilgan ma'lumotni ilova o'zgartirmaydi, Vazifa klassi o'z nusxasini yasaydi.

JS modullar turni yangi fayldan oladi. vazifa.js boshida @typedef (7 qator) o'rniga bitta qator:

js
// @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:

js
/** @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:

json
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true,
  • erasableSyntaxOnly ("faqat o'chiriladigan sintaksis") — testlar .ts fayllarni 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 (enum va boshqalar) taqiqlanadi. Ular haqida — enum va uning muqobillari darsida.
  • verbatimModuleSyntax — faqat turni import qilsangiz, buni aniq yozasiz: import type { … }. Build'da bunday import butunlay o'chadi. Uni keyingi qadamda birinchi .ts modulda ishlatamiz.

ESLint .ts uchun. O'rnatish — bitta buyruq:

bash
npm install --save-dev --save-exact \
  "@typescript/native@npm:typescript@7.0.2" \
  "typescript@npm:@typescript/typescript6@6.0.2" \
  typescript-eslint@8.71.1

package.json da shunday bo'ladi:

json
    "@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:

js
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:

js
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:

bash
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 --merge

Sarlavha — 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/ — vazifalar dagi turlar.ts kabi. Komponentlar va funksiyalar ularni import type bilan oladi.
  • Kutubxonalar (React, Express) ommaviy turlarini ko'pincha interface bilan yozadi — aynan siz ularni declaration merging bilan to'ldira olishingiz uchun.
  • Jamoa uslubi — type yoki interface tanlovi odatda ESLint qoidasi bilan mustahkamlanadi: typescript-eslint'da consistent-type-definitions qoidasi bor.
  • Intervyu: "type va interface farqi?" — 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. extends bilan kengayadi, bir xil nomli e'lonlar birlashadi (declaration merging).
  • interface ga yashirin index signature berilmaydi: Record<string, unknown> ga mos emas (TS2345, TS2677). type ga beriladi.
  • Kurs qoidasi: ma'lumot shakllari — type; kengaytirish yoki birlashtirish kerak bo'lsa — interface. Bitta loyihada — bitta qoida.
  • Turlar faqat tekshiruvda yashaydi: turlar.ts build'dan keyin export {}; 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
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
TypeScript type va interface: turga nom berish va qaysi birini tanlash — IlmHamroh