IlmHamroh
JavaScript Full-stack/15-qism. TypeScript4/47-dars24 daqiqa
Mundarija (34)

TypeScript massiv va tuple turlari: number[], readonly va as const

Qisqacha: Massiv turi — bir xil turdagi elementlar ro'yxati: number[] yoki Array<number> (ikkalasi bir xil). readonly number[] — o'zgartirib bo'lmaydigan massiv. Tuple — uzunligi va har o'rnining turi aniq bo'lgan massiv: [string, number] — "avval nom, keyin narx". as const esa massivni o'zgarmas tuple'ga aylantiradi va qiymatlarni aniq saqlaydi.

Bu darsda

  • Massiv turini ikki xil yozasiz va qaysi biri qachon qulayligini bilasiz.
  • readonly massiv bilan funksiyaga "men bu massivni o'zgartirmayman" degan va'da berasiz.
  • Tuple yozasiz: oddiy, nomlangan, ixtiyoriy va rest elementli.
  • as const nima qilishini va o'tgan darsdagi literal turlar bilan bog'liqligini tushuntira olasiz.
  • TypeScript ushlamaydigan indeks tuzog'ini taniysiz va undan saqlanasiz.

Oldin bilishingiz kerak: Tur annotatsiyasi va tur chiqarish, Massiv: yaratish va elementga murojaat, Massiv destructuring.

1. Nega bu kerak?

O'tgan darsda TypeScript turni qiymatdan o'zi chiqarishini ko'rdik. Endi massivlarga o'tamiz — «Bahor» kodining katta qismi aynan ro'yxatlar: menyu, narxlar, buyurtmalar, bronlar.

Sardor kunlik tushumni hisoblayapti. Narxlardan biri jadvaldan nusxa olinganda satr bo'lib qolgan:

js
const prices = [35000, "28 000", 30000];
const sum = prices.reduce((total, p) => total + p, 0);
console.log(sum); // 3500028 00030000

Kutilgan natija — 93 000. Chiqqani — uzun g'alati satr. 35000 + "28 000" satr berdi, keyin unga 30000 yopishtirildi. JavaScript massivga istalgan turdagi narsani solishga ruxsat beradi va hech narsa demaydi.

Ikkinchi muammo nozikroq. Jasur aka eng arzon taomni so'radi va Sardor funksiya yozdi:

js
function cheapest() {
  return ["Ko'k choy", 5000];
}

const result = cheapest();
console.log(result[1]); // 5000

result[0] — nommi yoki narxmi? Buni faqat funksiya ichiga qarab bilasiz. Uch oydan keyin kimdir tartibni almashtirib qo'ysa ([5000, "Ko'k choy"]), hamma chaqiruvlar jim buziladi.

Bu darsda ikkala muammoni ham turlar bilan hal qilamiz: birinchisini massiv turi, ikkinchisini tuple bilan.

2. Massiv turi

2.1 number[] — "sonlar massivi"

Yozuv sizga JSDoc darsidan tanish: element turi va kvadrat qavslar.

ts
const prices: number[] = [35000, 28000, 30000];
const dishes = ["Osh", "Manti", "Lag'mon"];
const total = prices.reduce((sum, p) => sum + p, 0);
console.log(total, dishes.length); // 93000 3

dishes ga tur yozmadik — TypeScript uni qiymatdan chiqardi: string[]. Hover'da const dishes: string[] ko'rinadi. reduce dagi sum va p ham kontekstdan chiqarildi — ikkalasi son.

Endi «Nega bu kerak?» bo'limidagi ikki xatoni qilib ko'ramiz:

ts
const prices: number[] = [35000, "28 000", 30000];
const dishes = ["Osh", "Manti"];
dishes.push(35000);

Kompilyator:

text
TS2322: Type 'string' is not assignable to type 'number'.
TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

Birinchi xabar: "string ni number ga berib bo'lmaydi" — tsc aynan "28 000" elementini ko'rsatadi, butun massivni emas. Ikkinchisi yangi: "number turidagi argumentni string turidagi parametrga berib bo'lmaydi". dishes — string[], demak uning push metodi faqat satr qabul qiladi. Bir marta string[] bo'lgan massivga son kira olmaydi.

2.2 Array<number> — o'sha tur, boshqa yozuv

ts
const prices: Array<number> = [35000, 28000];
const names: string[] = ["Malika", "Bobur"];
console.log(prices.length + names.length); // 4

Array<number> va number[] — aynan bir xil tur. Burchak qavs <…> "ichida nima bor" degani: JSDoc darsidagi Set<number> ("sonlar to'plami") kabi. Bu yozuv generic deyiladi — turning ichiga boshqa tur beriladi. Generic'larni o'zingiz yozishni Generics: turni parametr qilish darsida o'rganasiz. Hozircha uni o'qiy olish yetarli.

Qaysi birini yozish kerak? Ko'pchilik jamoalarda qisqasi — number[]. Element turi murakkab bo'lsa, ba'zan Array<…> o'qishga osonroq:

Ma'nosi Qisqa yozuv Generic yozuv
sonlar massivi number[] Array<number>
son yoki satrlar massivi (string | number)[] Array<string | number>
o'zgarmas satrlar massivi readonly string[] ReadonlyArray<string>

Ikkinchi qatordagi qavsga e'tibor bering. U muhim:

ts
const mixed: string | number[] = ["Osh", 35000];

Kompilyator:

text
TS2322: Type '(string | number)[]' is not assignable to type 'string | number[]'.
  Type '(string | number)[]' is not assignable to type 'number[]'.
    Type 'string | number' is not assignable to type 'number'.
      Type 'string' is not assignable to type 'number'.

Qavssiz string | number[] — "satr yoki sonlar massivi". Ya'ni yo bitta satr, yo faqat sonlardan iborat massiv. Aralash massiv bunga mos emas. To'rt qatorli zanjir pastga qarab torayib boradi: eng pastki qator — eng aniq sabab, "massiv ichidagi satrni songa berib bo'lmaydi". Qoida: | li element turini massivga aylantirsangiz — qavsga oling: (string | number)[]. Yoki adashmaslik uchun generic yozuvni tanlang.

2.3 Aralash massiv qanday chiqariladi?

ts
const row = ["Osh", 35000];
console.log(row); // [ 'Osh', 35000 ]

Hover: const row: (string | number)[]. TypeScript har bir element turini yig'ib, ulardan birlashma (union) yasadi. U row[0] satr, row[1] esa son ekanini eslab qolmaydi: uning uchun har bir element "satr yoki son". Bu tartibni eslab qoladigan tur — tuple, uni «Tuple» bo'limida ko'ramiz.

2.4 Bo'sh massivga tur yozing

Massiv bo'sh boshlansa, TypeScript element turini bilmaydi:

ts
const orderIds = [];
orderIds.push(101);
function lastOrders() {
  return orderIds;
}

Kompilyator:

text
TS7034: Variable 'orderIds' implicitly has type 'any[]' in some locations where its type cannot be determined.
TS7005: Variable 'orderIds' implicitly has an 'any[]' type.

Tarjimasi: "orderIds ba'zi joylarda turi aniqlab bo'lmaydigan any[] turini yashirincha oldi". TypeScript bo'sh massivning keyingi push larini bir fayl ichida kuzatishga harakat qiladi. Lekin massiv funksiyaga "chiqib ketsa", u taslim bo'ladi va any[] — "istalgan narsalar massivi" — deydi. strict rejim esa yashirin any ga ruxsat bermaydi.

Tuzatish oddiy — bo'sh massivga doim tur yozing. Bu o'tgan darsdagi "qiymatsiz e'lon — tur yozing" qoidasining o'zi: bo'sh massivda ham element qiymati yo'q.

ts
const orderIds: number[] = [];
orderIds.push(101);
orderIds.push(102);
console.log(orderIds); // [ 101, 102 ]

Tekshirib ko'ring: const scores = [4.5, 5, 3]; — hover'da qaysi tur? const tags = ["achchiq", 1, true]; chi?

Javob

const scores: number[] — butun va kasr sonlar bir xil tur. const tags: (string | number | boolean)[] — uch xil element, TypeScript ulardan birlashma yasadi. Ikkinchi holat odatda xato belgisi: bitta massivda turli ma'nodagi narsalar aralashgan. Ularni obyekt yoki tuple qilish to'g'riroq.

3. readonly massiv

3.1 O'zgartirib bo'lmaydigan ro'yxat

Menyu kun davomida o'zgarmaydi. Uni hech kim tasodifan buzmasligi uchun turga readonly (faqat o'qish uchun) so'zini qo'shamiz:

ts
const menu: readonly string[] = ["Osh", "Manti"];
menu.push("Lag'mon");
menu[0] = "Palov";
menu.sort();

Kompilyator:

text
TS2339: Property 'push' does not exist on type 'readonly string[]'.
TS2542: Index signature in type 'readonly string[]' only permits reading.
TS2339: Property 'sort' does not exist on type 'readonly string[]'.
  • push va sort yo'q: "readonly string[] turida push xususiyati yo'q". TypeScript massivni o'zgartiradigan hamma metodlarni (push, pop, splice, sort, reverse…) bu turdan olib tashlagan.
  • menu[0] = … — "indeks orqali faqat o'qish mumkin". Indeks imzosi (index signature) — menu[0], menu[1] kabi raqam bilan murojaat qilish qoidasi.

O'qiydigan va yangi massiv qaytaradigan metodlar esa joyida: map, filter, slice va nusxa bilan ishlaydiganlar — toSorted, toReversed, with:

ts
const menu: readonly string[] = ["Osh", "Manti", "Lag'mon"];
const sorted = menu.toSorted();
console.log(sorted); // [ "Lag'mon", 'Manti', 'Osh' ]
console.log(menu); // [ 'Osh', 'Manti', "Lag'mon" ]

Asl menyu o'zgarmadi. sorted esa oddiy (o'zgartirsa bo'ladigan) string[] — u yangi massiv.

3.2 Parametrda readonly — va'da

readonly ning eng foydali joyi — funksiya parametri:

ts
function totalOf(prices: readonly number[]): number {
  return prices.reduce((sum, p) => sum + p, 0);
}

const today = [35000, 28000];
console.log(totalOf(today)); // 63000

readonly number[] parametr chaqiruvchiga aytadi: "massivingizni beraveringiz, men uni o'zgartirmayman". Oddiy number[] ni ham bemalol berish mumkin — today o'zgaruvchan massiv, lekin funksiya uni faqat o'qiydi. Funksiya ichida prices.push(…) deb yozsangiz, tsc darhol to'xtatadi.

Teskarisi ishlamaydi. O'zgarmas massivni "o'zgartiraman" degan funksiyaga berib bo'lmaydi:

ts
function sortPrices(prices: number[]) {
  return prices.sort();
}
const fixed: readonly number[] = [35000, 5000];
sortPrices(fixed);

Kompilyator:

text
TS4104: The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.

Tarjimasi: "readonly number[] turi faqat o'qish uchun, uni o'zgaruvchan (mutable) number[] turiga berib bo'lmaydi". sortPrices massivni joyida saralaydi (Element qo'shish va olib tashlash darsidagi mutatsiya) — readonly va'dasi buzilardi.

Qoida: massivni faqat o'qiydigan funksiyada parametrni readonly qiling. Shunda funksiya har ikki xil massivni ham qabul qiladi.

3.3 readonly faqat tekshiruvda yashaydi

Bu muhim: readonly — tur, JavaScript emas. tsc 7.0.2 const menu: readonly string[] = ["Osh", "Manti"]; qatorini shunday yozadi:

js
const menu = ["Osh", "Manti"];

Ishlash paytida bu oddiy massiv. Kimdir uni turlarsiz JavaScript fayldan o'zgartirsa, hech kim to'xtatmaydi. Ishlash paytida ham himoya kerak bo'lsa — Object.freeze:

ts
const menu = Object.freeze(["Osh", "Manti"]);
console.log(Object.isFrozen(menu)); // true

Hover: const menu: readonly string[]. Object.freeze ikkalasini beradi: TypeScript turini (readonly) va ishlash paytidagi himoyani. Odatda esa readonly turning o'zi yetarli: tekshiruvdan o'tgan kod uni buzmaydi.

Tekshirib ko'ring: function firstDish(menu: readonly string[]) { return menu[0]; } — bu funksiyaga ["Osh", "Manti"] oddiy massivini berish mumkinmi?

Javob

Mumkin. readonly parametr — funksiyaning va'dasi: "o'zgartirmayman". Oddiy massivni o'qish uchun berishda xavf yo'q. Taqiqlangan yo'nalish — teskarisi: readonly massivni o'zgaruvchan string[] kutgan funksiyaga berish (TS4104).

4. Tuple

4.1 Aniq uzunlik, aniq o'rinlar

Tuple (o'qilishi "tapl") — uzunligi aniq va har bir o'rnining o'z turi bor massiv. Kunlik o'xshatish — chek qatori: avval taom nomi, keyin narxi. O'rinlar almashsa, chek ma'nosiz bo'ladi.

ts
const item: [string, number] = ["Osh", 35000];
console.log(`${item[0]}: ${item[1]} so'm`); // Osh: 35000 so'm

[string, number] — "ikki elementli massiv: 0-o'rinda satr, 1-o'rinda son". Endi TypeScript item[0] ni string, item[1] ni number deb biladi — (string | number)[] dagi kabi aralashtirmaydi.

Tuple uch xil xatoni ushlaydi — tartib, uzunlik va mavjud bo'lmagan o'rin:

ts
const item: [string, number] = [35000, "Osh"];
const short: [string, number] = ["Osh"];
const pair: [string, number] = ["Osh", 35000];
console.log(pair[2]);

Kompilyator:

text
TS2322: Type 'number' is not assignable to type 'string'.
TS2322: Type 'string' is not assignable to type 'number'.
TS2322: Type '[string]' is not assignable to type '[string, number]'.
  Source has 1 element(s) but target requires 2.
TS2493: Tuple type '[string, number]' of length '2' has no element at index '2'.
  • Birinchi ikkitasi — tartib almashgan: 0-o'ringa son, 1-o'ringa satr berilgan.
  • Uchinchisi: "manbada 1 ta element, maqsad esa 2 ta talab qiladi".
  • To'rtinchisi: "uzunligi 2 bo'lgan tuple'da 2-indeksli element yo'q". Indekslar 0 dan boshlanadi — 0 va 1 bor, 2 yo'q. Oddiy massivda bu xato chiqmasdi.

4.2 Tuple o'zi chiqarilmaydi

«Nega bu kerak?» bo'limidagi cheapest ni TypeScript'ga ko'chiramiz:

ts
function cheapest() {
  return ["Ko'k choy", 5000];
}
const [name, price] = cheapest();
const doubled = price * 2;

Kompilyator:

text
TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

Tarjimasi: "Arifmetik amalning chap tomoni any, number, bigint yoki enum turida bo'lishi kerak". Nega price son emas? Chunki TypeScript ["Ko'k choy", 5000] dan tuple emas, oddiy massiv chiqardi: (string | number)[] (aralash massiv kabi). Destructuring'dagi price ham "satr yoki son" bo'lib qoldi, satrni esa 2 ga ko'paytirib bo'lmaydi. (bigint — juda katta butun sonlar turi, enum — enum va uning muqobillari darsida.)

Tuzatish — natija turini yozish. Bu o'tgan darsdagi qoida bilan bir xil: TypeScript niyatingizni bilmaydi, siz aytasiz:

ts
function cheapest(): [string, number] {
  return ["Ko'k choy", 5000];
}
const [name, price] = cheapest();
console.log(name, price * 2); // Ko'k choy 10000

Endi name — string, price — number. Massiv destructuring tuple bilan ayniqsa qulay: o'rinlarga darhol tushunarli nom berasiz.

4.3 Nomlangan tuple elementlari

[string, number] dan har o'rin nimani anglatishi ko'rinmaydi. Ularga nom (label) berish mumkin:

ts
function workingHours(): [open: number, close: number] {
  return [7, 23];
}
const [from, to] = workingHours();
console.log(`${from}:00–${to}:00`); // 7:00–23:00

open va close — faqat hujjat. Ular hover'da va chaqiruvda maslahat sifatida ko'rinadi ([open: number, close: number]), lekin destructuring nomlariga ta'sir qilmaydi: biz from va to deb oldik. Ishlash paytida ham ular yo'q — bu oddiy [7, 23] massivi.

vazifalar loyihasida ham aynan shunday tur bor (qidiruv.ts, Generic cheklovlar va keyof qadamida .ts ga o'tadi):

ts
/** Asl matndagi [boshi, oxiri) — oxiri kirmaydi */
export type Oraliq = [boshi: number, oxiri: number];

type Oraliq = … — turga nom berish: endi har joyda uzun yozuv o'rniga Oraliq deyiladi. Bu yozuvni type va interface darsida to'liq o'rganamiz. Hozir faqat o'ng tomonga qarang — nomlangan tuple: qidiruvda topilgan bo'lakning boshi va oxiri.

4.4 Ixtiyoriy element

Buyurtma qatorida izoh bo'lishi ham, bo'lmasligi ham mumkin. Ixtiyoriy o'rin ? bilan belgilanadi:

ts
const plain: [dish: string, qty: number, note?: string] = ["Osh", 2];
const custom: [dish: string, qty: number, note?: string] = [
  "Manti",
  3,
  "achchiqsiz",
];
console.log(plain.length, custom.length); // 2 3
console.log(plain[2] ?? "izohsiz"); // izohsiz

Hover'da plain.length turi — 2 | 3: "uzunligi 2 yoki 3". plain[2] turi esa string | undefined — izoh bo'lmasligi ham mumkin, shuning uchun ?? bilan standart matn berdik. Ixtiyoriy elementlar faqat oxirida turadi: [note?: string, dish: string] yozib bo'lmaydi.

4.5 Rest element: "qolgani — shu turdagi"

Narxlar tarixi: avval taom nomi, keyin nechta bo'lsa ham narx. Tuple ichida ... (rest) ishlatiladi — Spread va rest darsidagi uch nuqta:

ts
const history: [string, ...number[]] = ["Osh", 30000, 32000, 35000];
const [dish, ...prices] = history;
console.log(dish, prices.length); // Osh 3
console.log(Math.max(...prices)); // 35000

[string, ...number[]] — "birinchisi satr, qolganlari — istalgancha son (nol ta ham)". ["Osh"] ham mos keladi. Destructuring'da prices — number[]. Rest o'rta yoki boshda ham bo'lishi mumkin: [...string[], number] — "oxirgisi son, oldingilari satrlar".

4.6 Tuple tuzog'i: push ruxsat etilgan

Tuple uzunligi tekshiriladi, lekin faqat yaratilganda. Keyin esa — kutilmagan narsa:

ts
const pair: [string, number] = ["Osh", 35000];
pair.push(5000);
const len: 2 = pair.length;
console.log(len); // 3
console.log(pair); // [ 'Osh', 35000, 5000 ]

tsc hech qanday xato bermadi. Turga ko'ra pair.length — aniq 2 (literal tur), haqiqatda esa 3. Sababi: tuple — massiv, massivda esa push metodi bor. TypeScript tuple'ning push iga ham element turini (string | number) tekshiradi, lekin uzunlik o'zgarishini kuzatmaydi.

Himoya — readonly tuple:

ts
const pair: readonly [string, number] = ["Osh", 35000];
pair.push(5000);

Kompilyator:

text
TS2339: Property 'push' does not exist on type 'readonly [string, number]'.

TypeScript aytyapti: «readonly [string, number] turida push xususiyati yo'q». Faqat o'qiladigan tuple'da o'zgartiruvchi metodlar umuman yo'q — push, pop, sort ham.

Qoida: tuple'ni o'zgartirmaydigan bo'lsangiz (ko'pincha shunday), uni readonly qiling.

Tekshirib ko'ring: Qaysi biri [string, ...number[]] turiga mos: ["Manti"], [30000, "Manti"], ["Manti", 28000, 30000]?

Javob

["Manti"] va ["Manti", 28000, 30000] mos: birinchi o'rinda satr, keyin nol yoki ko'p son. [30000, "Manti"] mos emas — birinchi o'rinda son turibdi, tartib muhim.

5. as const: massivni aniq qilib qotirish

O'tgan darsda const city = "Toshkent" ning turi "Toshkent" bo'lishini, lekin obyekt ichida qiymatlar kengayishini ko'rdik. Massivda ham xuddi shunday: const sizes = ["kichik", "o'rta", "katta"] — string[]. Qiymatlar unutiladi.

as const ("o'zgarmas sifatida") TypeScript'ga aytadi: "bu qiymatni aynan shunday eslab qol — kengaytirma va o'zgartirishga ruxsat berma":

ts
const sizes = ["kichik", "o'rta", "katta"] as const;
const dish = ["Osh", 35000] as const;
console.log(sizes.length, dish[1]); // 3 35000
Kod Hover'da
["kichik", "o'rta", "katta"] string[]
["kichik", "o'rta", "katta"] as const readonly ["kichik", "o'rta", "katta"]
["Osh", 35000] (string | number)[]
["Osh", 35000] as const readonly ["Osh", 35000]

as const uch ish qiladi:

  1. Massivni tuple qiladi — uzunlik va o'rinlar eslab qolinadi.
  2. Elementlarni literal tur qiladi — "kichik", 35000, string va number emas.
  3. Hammasini readonly qiladi:
ts
const sizes = ["kichik", "o'rta", "katta"] as const;
sizes.push("juda katta");

Kompilyator:

text
TS2339: Property 'push' does not exist on type 'readonly ["kichik", "o'rta", "katta"]'.

Qachon kerak? O'zgarmaydigan, oldindan ma'lum ro'yxatlarda: porsiya o'lchamlari, buyurtma holatlari, hafta kunlari. Bunday ro'yxatdan "faqat shu uch qiymatdan biri" degan tur yasash mumkin — buni Union va literal turlar darsida qilamiz. Yana bir eslatma: as const ham faqat tur. Ishlash paytida massiv oddiy bo'lib qoladi, Object.freeze dagi himoya yo'q. Masalan, tsc uni const sizes = ["kichik", "o'rta", "katta"]; qilib yozadi.

Tekshirib ko'ring: const hours = [10, 22] as const; — hours[0] ning turi qanday? hours[0] = 9 yozsa bo'ladimi?

Javob

hours[0] turi — 10 (literal tur), chunki as const qiymatlarni aniq saqlaydi. hours[0] = 9 — xato: as const massivni readonly qiladi, indeks orqali yozib bo'lmaydi. Agar yozib bo'lganda ham, 9 qiymati 10 turiga mos kelmasdi.

6. TypeScript ushlamaydigan tuzoq: mavjud bo'lmagan indeks

Tuple'da pair[2] xato edi. Oddiy massivda-chi?

ts
const prices = [35000, 28000];
const p = prices[5];
console.log(p.toFixed(0));

tsc — xatosiz. Node esa:

text
TypeError: Cannot read properties of undefined (reading 'toFixed')

Hover'da const p: number — TypeScript prices[5] ni son deb hisobladi. Massivda 2 ta element, 5-indeksda hech narsa yo'q, JavaScript esa bunday holatda undefined qaytaradi (Massiv: yaratish va elementga murojaat). Ya'ni tur yolg'on gapirdi.

Nega TypeScript shunday qiladi? Massiv uzunligi ishlash paytida o'zgaradi, kompilyator uni bilmaydi. Har prices[i] ni "number | undefined" desa, eng oddiy sikllar ham tekshiruvga to'lib ketardi. TypeScript jamoasi standart holatda qulaylikni tanlagan.

Lekin buni yoqadigan maxsus bayroq bor — noUncheckedIndexedAccess ("tekshirilmagan indeks murojaati yo'q"). Uni blokning birinchi qatoridagi sozlama bilan yoqib ko'ramiz:

ts
// @ts-options: {"noUncheckedIndexedAccess": true}
const prices = [35000, 28000];
const p = prices[5];
console.log(p.toFixed(0));

Kompilyator:

text
TS18048: 'p' is possibly 'undefined'.

Tarjimasi: "p undefined bo'lishi mumkin". Endi prices[5] ning turi — number | undefined, va TypeScript tekshiruvsiz ishlatishga yo'l qo'ymaydi. Tuzatish — JSDoc darsidagi kabi, turni toraytirish:

ts
// @ts-options: {"noUncheckedIndexedAccess": true}
const prices = [35000, 28000];
const p = prices[5];
if (p === undefined) {
  console.log("Bunday narx yo'q"); // Bunday narx yo'q
} else {
  console.log(p.toFixed(0));
}

Bu bayroq haqida uch fakt:

  • strict ga kirmaydi — alohida yoqiladi. Saytimizdagi bloklar va Playground'da u standart holatda o'chiq.
  • tsc --init (TypeScript 7) uni yoqib qo'yadi — yangi tsconfig.json da "noUncheckedIndexedAccess": true qatori bor. Mashqlar papkangizda ham yoqilgan.
  • for...of, map, forEach ga ta'sir qilmaydi — ular faqat mavjud elementlar bo'ylab yuradi.

vazifalar ham uni yoqadi (Strict bayroqlar oilasi qadamida) va qidiruv.ts da shunday tekshiruvlar paydo bo'ladi: const birinchi = joylar[joy]; if (birinchi === undefined) …. Bayroqni va uning "narxini" o'sha darsda to'liq ko'ramiz.

7. Massivmi yoki tuple?

Tanlash uchun bitta savol yetadi: har bir o'rinning o'z ma'nosi bormi?

flowchart TD
  A["Bir nechta qiymat"] --> B{"Har o'rinning<br/>o'z ma'nosi bormi?"}
  B -- "yo'q, hammasi bir xil narsa" --> C["Massiv: number[]"]
  B -- "ha: nom, narx, ..." --> D{"Elementlar 3 tadan ko'pmi?"}
  D -- "yo'q" --> E["Tuple: [string, number]"]
  D -- "ha" --> F["Obyekt: { name, price }"]
  C --> G{"O'zgartirilmaydimi?"}
  E --> G
  G -- "ha" --> H["readonly yoki as const"]

Menyu narxlari — hammasi "narx", demak massiv. "Nom va narx" — ikki xil ma'no, demak tuple. Lekin o'rinlar ko'payib ketsa ([string, number, string, boolean, number]), [3] nima ekanini hech kim eslay olmaydi — unda nomli maydonlari bor obyekt yaxshiroq (Obyekt turlari). Tuple — qisqa, 2–3 o'rinli juftliklar uchun.

8. Ko'p uchraydigan xatolar

8.1 [] ni "massiv" turi deb yozish

ts
let names: [] = [];
names.push("Ali");

Kompilyator:

text
TS2345: Argument of type '"Ali"' is not assignable to parameter of type 'never'.

[] turi — "massiv" emas, bo'sh tuple: nol elementli. Unga hech narsa qo'shib bo'lmaydi, shuning uchun push parametri — never ("hech qanday qiymat bo'la olmaydi"; never haqida keyinroq). Tuzatish: element turini yozing — let names: string[] = [];.

8.2 Array ni ichsiz yozish

ts
let list: Array = [];

Kompilyator:

text
TS2314: Generic type 'Array<T>' requires 1 type argument(s).

Tarjimasi: "Array<T> generic turi 1 ta tur argumentini talab qiladi". T — "bu yerga tur qo'yiladi" degan joy. Tuzatish: Array<string> yoki string[].

8.3 string | number[] qavssiz

«Array<number> — o'sha tur, boshqa yozuv» bo'limidagi to'rt qatorli xabar. Tuzatish: (string | number)[].

8.4 Funksiyadan tuple qaytarib, natija turini yozmaslik

return ["Ko'k choy", 5000] — tuple emas, (string | number)[]. Tuzatish: natija turi : [string, number] yoki as const.

8.5 Indeksga ko'r-ko'rona ishonish

prices[i] — number, lekin undefined bo'lishi mumkin. Tuzatish: uzunlikni yoki undefined ni tekshiring, loyihada noUncheckedIndexedAccess ni yoqing.

9. Mashqlar

1-mashq (oson): Turlarni ayting

ts
const sizes = ["S", "M", "L"] as const;
const pair: [string, number] = ["Somsa", 8000];
const ids: number[] = [];
console.log(sizes.length, pair.length, ids.length); // 3 2 0

sizes.length turi (literal): [:3]. pair.length turi: [:2]. Bayroqsiz ids[0] turi: .

Yechim

as const tuple yasaydi, tuple uzunligi esa literal tur: 3. pair — ikki elementli tuple, length turi 2. ids[0] — number: noUncheckedIndexedAccess siz TypeScript bo'sh massivning ham elementini son deb hisoblaydi (garchi haqiqatda undefined bo'lsa ham). Bayroq yoqilsa — number | undefined.

2-mashq (o'rta): minMax ni tuzating

Bu funksiya tsc dan xatosiz o'tadi, lekin bo'sh massivda jim NaN beradi:

ts
function minMax(prices: number[]) {
  let min = prices[0];
  let max = prices[0];
  for (const p of prices) {
    if (p < min) min = p;
    if (p > max) max = p;
  }
  return [min, max];
}

const [low, high] = minMax([]);
console.log(high - low); // NaN

Uchta o'zgarish qiling: (1) parametr readonly bo'lsin — funksiya massivni o'zgartirmaydi; (2) natija nomlangan tuple bo'lsin: [min: number, max: number]; (3) bo'sh massivda RangeError tashlansin. Ishora: bo'sh massivni prices.length === 0 bilan tekshirish mumkin, lekin noUncheckedIndexedAccess bilan ham ishlaydigan yo'l — prices[0] ni o'zgaruvchiga olib, undefined ni tekshirish.

Yechim
ts
// @ts-options: {"noUncheckedIndexedAccess": true}
function minMax(
  prices: readonly number[],
): [min: number, max: number] {
  const first = prices[0];
  if (first === undefined) {
    throw new RangeError("Narxlar ro'yxati bo'sh");
  }
  let min = first;
  let max = first;
  for (const p of prices) {
    if (p < min) min = p;
    if (p > max) max = p;
  }
  return [min, max];
}

const [low, high] = minMax([35000, 28000, 30000, 5000]);
console.log(low, high); // 5000 35000
  • readonly number[] — oddiy massiv ham, as const ro'yxat ham beriladi.
  • Natija turi tuple'ni majburiy qildi: usiz [min, max] — number[], hover'da o'rinlar nomi ham ko'rinmasdi.
  • first ni tekshirdik — noUncheckedIndexedAccess yoqilgan bo'lsa ham tsc toza (biz uni blokning birinchi qatorida yoqdik). prices.length === 0 bilan esa bayroq yoqilganda prices[0] hali ham number | undefined bo'lib qolardi: TypeScript uzunlik tekshiruvidan indeksni toraytirmaydi.

3-mashq (qiyin): Amaliy tajriba — bron vaqtlari

Mashq o'tgan darsda tayyorlangan kurs/mashqlar/15/ papkasida bajariladi. U yerdagi tsconfig.json da noUncheckedIndexedAccess yoqilgan. 04-massiv-tuple/booking.ts faylini yarating:

  • parseTime(text) — "19:30" → [19, 30], natija turi [hour: number, minute: number]. Buzuq matn ("19", "yetti:30") — RangeError.
  • isBookable(time) — bron 10:00 dan 22:00 gacha (22:00 ham mumkin). Parametr — o'zgarmas ikki sonli tuple.
  • totalPeople(bookings) — [mehmon, odamlar soni] juftliklari ro'yxatidan jami odam. Parametr — o'zgarmas.

Ishora: text.split(":").map(Number) — satrlar massivini sonlar massiviga aylantiradi; Number("yetti") — NaN, uni Number.isNaN bilan tekshirasiz.

Avval birinchi variantni — tekshiruvsiz destructuring bilan — yozib, npx tsc --pretty false ni ishga tushiring:

ts
// 04-massiv-tuple/booking.ts — «Bahor» bronlari
export function parseTime(
  text: string,
): [hour: number, minute: number] {
  const [hour, minute] = text.split(":").map(Number);
  return [hour, minute];
}

Keyin 04-massiv-tuple/booking.test.ts da testlar yozing. Nimalarni tekshirish kerak: to'g'ri vaqt va ikki buzuq vaqt; 10:00, 22:00, 22:01 va 09:59 chegaralari; bronlar yig'indisi, bo'sh ro'yxat ham.

Yechim

Birinchi variantda tsc shunday dedi:

text
04-massiv-tuple/booking.ts(6,11): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
  Type 'undefined' is not assignable to type 'number'.
04-massiv-tuple/booking.ts(6,17): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
  Type 'undefined' is not assignable to type 'number'.

Bu noUncheckedIndexedAccess ishi: "19".split(":") — bitta elementli massiv, minute esa undefined. Bayroq bo'lmaganda tsc jim turar, funksiya [19, undefined] qaytarardi va isBookable NaN bilan solishtirib, false berardi — sababsiz "bron mumkin emas".

ts
// 04-massiv-tuple/booking.ts — «Bahor» bronlari
export type Booking = readonly [guest: string, people: number];

// "19:30" → [19, 30]; buzuq matn — RangeError
export function parseTime(
  text: string,
): [hour: number, minute: number] {
  const [hour, minute] = text.split(":").map(Number);
  if (hour === undefined || minute === undefined) {
    throw new RangeError(`Vaqt "SS:DD" bo'lsin: ${text}`);
  }
  if (Number.isNaN(hour) || Number.isNaN(minute)) {
    throw new RangeError(`Vaqt "SS:DD" bo'lsin: ${text}`);
  }
  return [hour, minute];
}

// Bron 10:00 dan 22:00 gacha (22:00 ham mumkin)
export function isBookable(time: readonly [number, number]): boolean {
  const minutes = time[0] * 60 + time[1];
  return minutes >= 10 * 60 && minutes <= 22 * 60;
}

export function totalPeople(bookings: readonly Booking[]): number {
  let sum = 0;
  for (const [, people] of bookings) {
    sum += people;
  }
  return sum;
}

Booking — juftlik turiga nom (type, xuddi Oraliq kabi). readonly Booking[] — "o'zgarmas juftliklarning o'zgarmas ro'yxati". for (const [, people] of …) — destructuring'da birinchi o'rinni vergul bilan o'tkazib yubordik. Tuple indekslari (time[0], time[1]) bayroq yoqilgan bo'lsa ham number — tuple'ning uzunligi aniq, undefined bo'lishi mumkin emas.

ts
// 04-massiv-tuple/booking.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { parseTime, isBookable, totalPeople } from "./booking.ts";

test("parseTime: matndan tuple", () => {
  assert.deepEqual(parseTime("19:30"), [19, 30]);
});

test("parseTime: buzuq matn — RangeError", () => {
  assert.throws(() => parseTime("19"), RangeError);
  assert.throws(() => parseTime("yetti:30"), RangeError);
});

test("isBookable: 10:00–22:00 chegaralari", () => {
  assert.equal(isBookable([10, 0]), true);
  assert.equal(isBookable([22, 0]), true);
  assert.equal(isBookable([22, 1]), false);
  assert.equal(isBookable(parseTime("09:59")), false);
});

test("totalPeople: bronlardagi odamlar", () => {
  const today = [
    ["Dilshod aka", 4],
    ["Malika", 2],
  ] as const;
  assert.equal(totalPeople(today), 6);
  assert.equal(totalPeople([]), 0);
});

today — as const: o'zgarmas, literal tuple'lar ro'yxati. U readonly Booking[] ga mos keladi. Agar totalPeople parametrini oddiy [string, number][] qilsangiz, tsc shu qatorni to'xtatadi:

text
04-massiv-tuple/booking.test.ts(27,28): error TS4104: The type 'readonly [readonly ["Dilshod aka", 4], readonly ["Malika", 2]]' is 'readonly' and cannot be assigned to the mutable type '[string, number][]'.

«Parametrda readonly — va'da» bo'limidagi qoidaning haqiqiy misoli. npx tsc --pretty false — chiqishsiz (chiqish kodi 0). Testlarni faqat shu fayl uchun ishga tushiramiz: node --test 04-massiv-tuple/booking.test.ts. (Papka nomini berish ishlamaydi: node --test 04-massiv-tuple/ uni fayl deb qidiradi va Cannot find module beradi.) Bizda (Node 24.21.0) shunday chiqdi:

text
✔ parseTime: matndan tuple (1.2691ms)
✔ parseTime: buzuq matn — RangeError (0.4075ms)
✔ isBookable: 10:00–22:00 chegaralari (0.1576ms)
✔ totalPeople: bronlardagi odamlar (0.1407ms)
ℹ tests 4
ℹ suites 0
ℹ pass 4
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 110.4381

XATOLAR.md ga bu darsning yangi xabarlarini qo'shing: TS2542, TS4104, TS2493, TS2362, TS7034, TS18048, TS2314. Keyin kurs/mashqlar papkasidan:

bash
git add 15/04-massiv-tuple 15/XATOLAR.md
git commit -m "15/04: booking.ts — tuple va readonly turlar, testlar"

10. Real ishda

  • React'dagi useState (React — interfeys yasash kutubxonasi, 17-qismdan o'rganamiz) tuple qaytaradi: const [count, setCount] = useState(0). Ikkita o'rin — qiymat va uni o'zgartiruvchi funksiya. Siz endi bu yozuv nima ekanini bilasiz.
  • Object.entries natijasi — tuple'lar massivi. Object.entries({ osh: 35000, manti: 30000 }) ning turi — [string, number][]: har juftlik — [kalit, qiymat].
  • Koordinatalar, oraliqlar, juftliklar: [lat, lng], [boshi, oxiri] (vazifalar dagi Oraliq), [soat, daqiqa] — tuple'ning tabiiy joyi.
  • readonly parametrlar — kutubxonalar va katta loyihalarda odatiy uslub: funksiya imzosidan uning massivni buzmasligi ko'rinib turadi.
  • Intervyu: "Array va tuple farqi?", "readonly va Object.freeze farqi?", "as const nima qiladi?" — tez-tez so'raladi.

Xulosa

  • Massiv turi — number[] yoki Array<number>, bir xil. | li element turi — qavsda: (string | number)[]. Bo'sh massivga tur yozing.
  • readonly number[] — push, sort, indeksga yozish yo'q. Massivni faqat o'qiydigan parametrda ishlating. Bu faqat tur — ishlash paytida himoya Object.freeze da.
  • Tuple — aniq o'rinlar: [string, number]. Nomlangan ([open: number, close: number]), ixtiyoriy (note?: string) va rest (...number[]) elementlar bo'ladi. TypeScript tuple'ni o'zi chiqarmaydi — natija turini yozing.
  • as const — literal turlar + tuple + readonly. O'zgarmas ro'yxatlar uchun.
  • Oddiy massivda arr[i] turi undefined ni hisobga olmaydi. noUncheckedIndexedAccess buni tuzatadi.

Keyingi dars: Obyekt turlari — obyektning shaklini ixtiyoriy va readonly xususiyatlar bilan yozamiz va TypeScript ortiqcha xususiyatlarni qanday ushlashini ko'ramiz.

Manbalar

  • TypeScript Handbook: "Everyday Types" (Arrays) — typescriptlang.org/docs/handbook/2/everyday-types.html#arrays
  • TypeScript Handbook: "Object Types" (The ReadonlyArray Type, Tuple Types, readonly Tuple Types) — typescriptlang.org/docs/handbook/2/objects.html#tuple-types
  • TypeScript Release Notes 3.4 (as const, readonly massivlar) va 4.0 (nomlangan tuple elementlari) — typescriptlang.org/docs/handbook/release-notes/
  • TSConfig: noUncheckedIndexedAccess — typescriptlang.org/tsconfig#noUncheckedIndexedAccess
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
TypeScript massiv va tuple turlari: number[], readonly va as const — IlmHamroh