Mundarija (38)
- Bu darsda
- 1. Nega bu kerak?
- 2. Index signature: "har qanday kalit, bir xil qiymat"
- 2.1 Yozuv
- 2.2 Qaysi kalit turlari mumkin?
- 2.3 Aniq maydon va imzo birga
- 2.4 readonly imzo
- 3. Yashirin undefined
- 3.1 TypeScript ushlamaydigan tuzoq
- 3.2 noUncheckedIndexedAccess lug'atda ham ishlaydi
- 3.3 Nuqta bilan murojaat: noPropertyAccessFromIndexSignature
- 4. Record<K, V>
- 4.1 Index signature'ning qisqa yozuvi
- 4.2 Aniq kalitlar: hammasi majburiy
- 4.3 Ba'zi kalitlar bo'lmasligi mumkin
- 4.4 vazifalar dagi Record
- 5. Obyekt aylanganda kalitlar string
- 5.1 Object.keys nega string[]?
- 5.2 string kalit bilan aniq Record ga murojaat
- 6. Oddiy obyekt lug'atining tuzog'i: meros kalitlari
- 6.1 "constructor" degan so'z
- 6.2 Himoya: prototipsiz obyekt yoki Map
- 6.3 Qachon nima?
- 7. interface va index signature
- 7.1 Yashirin imzo faqat type da
- 8. Ko'p uchraydigan xatolar
- 8.1 Index signature'da literal union
- 8.2 Aniq maydon imzo turiga mos emas
- 8.3 Lug'atdan olingan qiymatga ko'r-ko'rona ishonish
- 8.4 Aniq Record ga string bilan murojaat
- 8.5 Foydalanuvchi kalitlari oddiy obyektda
- 9. Mashqlar
- 1-mashq (oson): Turni ayting
- 2-mashq (o'rta): Ish vaqti jadvali
- 3-mashq (qiyin): Amaliy tajriba — buyurtma hisobi
- 10. Real ishda
- Xulosa
- Manbalar
TypeScript index signature va Record: kalitlari noma'lum obyektlar
Qisqacha: Kalitlari oldindan noma'lum obyekt (lug'at) shunday tiplanadi:
{ [dish: string]: number }yoki qisqasiRecord<string, number>. Kalitlar ro'yxati aniq bo'lsa —Record<"osh" | "manti", number>: unda hamma kalit majburiy. Tuzoq:prices["lagmon"]turinumber, haqiqatda esaundefinedbo'lishi mumkin —noUncheckedIndexedAccessyokiMaphimoya qiladi.
Bu darsda
- Index signature (
[kalit: string]: T) yozasiz va uning cheklovlarini bilasiz. Record<K, V>ni ikki xil ishlatasiz: istalgan kalitli lug'at va aniq kalitlar ro'yxati.- Lug'atdagi yashirin
undefinedni ko'rasiz vanoUncheckedIndexedAccessbilan ushlaysiz. - Nega
interfaceRecord<string, unknown>ga mos kelmasligini tushuntira olasiz. - Qachon obyekt o'rniga
Mapolish kerakligini tanlay olasiz.
Oldin bilishingiz kerak: Kengaytirish, kesishma va declaration merging, Massiv va tuple turlari, Map, Union va literal turlar.
1. Nega bu kerak?
O'tgan darsda maydonlari aniq turlarni birlashtirdik: name, price, cookMinutes. Lekin ba'zi obyektlarda kalitlar oldindan ma'lum emas. Masalan, «Bahor» narxlar ro'yxati: taom nomi — kalit, narx — qiymat. Ertaga Jasur aka yangi taom qo'shadi, kalitlar ko'payadi.
Sardor buyurtma summasini hisoblaydi. Kassir "lagmon" ni "lag'mon" deb yozib yubordi:
const prices = { osh: 35000, manti: 30000, lagmon: 28000 };
const order = ["osh", "lag'mon"];
const total = order.reduce((sum, name) => sum + prices[name], 0);
console.log(total); // NaNprices["lag'mon"] — undefined, summa esa NaN. Bunday obyekt lug'at (dictionary) deyiladi: kitob lug'atidagi kabi, so'z bo'yicha qiymat topiladi. Lug'atda kalit bo'lmasligi — odatiy holat, xato emas. Bu darsda lug'atni to'g'ri tiplashni va "kalit yo'q" holatini TypeScript'ga ko'rsatishni o'rganamiz.
2. Index signature: "har qanday kalit, bir xil qiymat"
2.1 Yozuv
Lug'at turi shunday yoziladi:
type PriceList = { [dish: string]: number };
const prices: PriceList = { osh: 35000, manti: 30000 };
prices["lagmon"] = 28000;
console.log(Object.keys(prices).length); // 3[dish: string]: number — index signature (indeks imzosi): "istalgan satr kalit, qiymati — son". Massiv turlari darsida readonly massivning "indeks imzosi" xabarini ko'rgansiz — bu o'sha narsa, endi obyekt uchun. Kvadrat qavs ichidagi dish — faqat nom, hujjat uchun: [key: string] yoki [x: string] deb yozsangiz ham tur bir xil. Lekin ma'noli nom kodni o'qishni osonlashtiradi.
Endi qiymat turi tekshiriladi:
type PriceList = { [dish: string]: number };
const prices: PriceList = { osh: 35000, manti: "30 000" };Kompilyator:
TS2322: Type 'string' is not assignable to type 'number'.Tarjimasi: "string turini number turiga berib bo'lmaydi". TypeScript manti ning qiymatiga qarab aytyapti: "bu yerda son kutgandim, matn keldi". Kalit istalgancha bo'lishi mumkin, lekin har bir qiymat son bo'lishi shart.
2.2 Qaysi kalit turlari mumkin?
Kalit turi faqat string, number, symbol yoki shablon satr turi (pastroqda ko'ramiz) bo'la oladi. Literal union yozib ko'ramiz:
type DishName = "osh" | "manti";
type Prices = { [dish: DishName]: number };Kompilyator:
TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.Tarjimasi: "Index signature parametri literal yoki generic tur bo'la olmaydi. O'rniga mapped object turidan foydalaning". Oddiy qilib: kvadrat qavs ichiga aniq nomlar ro'yxatini yozib bo'lmaydi. Xabardagi ikki yangi so'zdan qo'rqmang. Generic — "turi keyin beriladigan tur", uni Generics darsida o'rganamiz. Mapped type — Mapped types darsida. Lekin uning eng ko'p ishlatiladigan tayyor shakli — Record — shu darsning o'zida, «Record<K, V>» bo'limida.
Kalit turi sifatida shablon satr turi (template literal type) ham mumkin. Bu JavaScript'dagi `Salom, ${ism}` shablonining tur darajasidagi egizagi: ${…} o'rnida qiymat emas, tur turadi. U bilan kalitning ko'rinishini cheklash mumkin. Masalan, HTML'dagi data- atributlari (Atributlar, xususiyatlar va dataset):
type DataAttrs = { [attr: `data-${string}`]: string };
const attrs: DataAttrs = { "data-id": "7", "data-dish": "osh" };
console.log(Object.keys(attrs)); // [ 'data-id', 'data-dish' ]`data-${string}` — "data- bilan boshlanadigan istalgan satr". Boshqa kalit yozilsa:
type DataAttrs = { [attr: `data-${string}`]: string };
const attrs: DataAttrs = { "data-id": "7", title: "Osh" };Kompilyator:
TS2353: Object literal may only specify known properties, and 'title' does not exist in type 'DataAttrs'.Tarjimasi: "Obyekt literalida faqat ma'lum xususiyatlar bo'lishi mumkin, title esa DataAttrs turida yo'q". Bunday shablon turlarini Template literal types darsida chuqur o'rganamiz.
number kalitli imzo ham bor: { [table: number]: string } — "stol raqami → ofitsiant". Lekin yodda tuting: JavaScript obyektida kalitlar baribir satr. Object.keys ["1", "2"] qaytaradi, [1, 2] emas. number imzosi faqat "bu yerga son bilan murojaat qilinadi" degan tur darajasidagi va'da.
2.3 Aniq maydon va imzo birga
Lug'atga bitta aniq maydon qo'shmoqchimiz — valyuta nomi:
type PriceList = {
[dish: string]: number;
currency: string;
};Kompilyator:
TS2411: Property 'currency' of type 'string' is not assignable to 'string' index type 'number'.Tarjimasi: "string turidagi currency xususiyatini string kalitli imzoning number turiga berib bo'lmaydi". Mantiqi oddiy: imzo "har qanday satr kalitning qiymati — son" deydi. "currency" ham satr kalit, demak uning qiymati ham son bo'lishi kerak edi.
Ikki yo'l bor. Qiymat turini kengaytirish (number | string) — lekin unda har bir narxni ishlatishdan oldin tekshirishga to'g'ri keladi. Yaxshisi — ma'lumotni ajratish:
type Menu = {
currency: string;
prices: { [dish: string]: number };
};
const menu: Menu = { currency: "so'm", prices: { osh: 35000 } };
console.log(menu.prices["osh"], menu.currency); // 35000 so'mLug'at — alohida maydonda, aniq maydonlar — tashqarida. Tur toza, ziddiyat yo'q.
2.4 readonly imzo
Narxlar kun davomida o'zgarmasligi kerak bo'lsa, imzo oldiga readonly qo'yiladi:
const prices: { readonly [dish: string]: number } = { osh: 35000 };
prices["osh"] = 30000;Kompilyator:
TS2542: Index signature in type '{ readonly [dish: string]: number; }' only permits reading.Bu Massiv va tuple turlari darsidagi readonly string[] xabarining aynan o'zi: "bu turdagi indeks imzosi faqat o'qishga ruxsat beradi".
Tekshirib ko'ring:
type Stock = { [dish: string]: number; total: number }— xato beradimi? Nega?
Javob
Xato bermaydi. total — satr kalit, qiymati number, imzo esa "har qiymat — son" deydi. Talab bajarildi. Muammo faqat aniq maydonning turi imzo turiga mos kelmaganda chiqadi (currency: string kabi).
3. Yashirin undefined
3.1 TypeScript ushlamaydigan tuzoq
«Nega bu kerak?» bo'limidagi xatoni TypeScript'da takrorlaymiz:
const prices: { [dish: string]: number } = { osh: 35000 };
const price = prices["lag'mon"];
console.log(price.toLocaleString("ru-RU"));tsc — xatosiz. Hover'da const price: number. Node esa:
TypeError: Cannot read properties of undefined (reading 'toLocaleString')Bu Massiv va tuple turlari darsidagi prices[5] tuzog'ining o'zi. Index signature "har qanday satr kalitning qiymati — son" deydi. TypeScript buni so'zma-so'z tushunadi: "lag'mon" kaliti ham bor va son deb hisoblaydi. Haqiqatda esa lug'atda bunday kalit yo'q.
3.2 noUncheckedIndexedAccess lug'atda ham ishlaydi
O'sha bayroq bu yerda ham yordam beradi:
// @ts-options: {"noUncheckedIndexedAccess": true}
const prices: { [dish: string]: number } = { osh: 35000 };
const price = prices["lag'mon"];
console.log(price.toLocaleString("ru-RU"));Kompilyator:
TS18048: 'price' is possibly 'undefined'.Tarjimasi: "price undefined bo'lishi mumkin". TypeScript aytyapti: "bu kalit lug'atda bo'lmasligi mumkin, avval tekshiring". Endi prices[…] ning turi — number | undefined. Tuzatish — null va undefined darsidagi usullar: ?? bilan standart qiymat yoki tekshiruv.
// @ts-options: {"noUncheckedIndexedAccess": true}
const prices: { [dish: string]: number } = { osh: 35000 };
function priceOf(dish: string): number {
const price = prices[dish];
if (price === undefined) {
throw new RangeError(`Menyuda yo'q: ${dish}`);
}
return price;
}
console.log(priceOf("osh")); // 35000
console.log(prices["lag'mon"] ?? 0); // 0Mashqlar papkangizdagi tsconfig.json da bu bayroq yoqilgan. Lug'at bilan ishlaganda u ayniqsa foydali: lug'atda kalit bo'lmasligi — kundalik holat.
3.3 Nuqta bilan murojaat: noPropertyAccessFromIndexSignature
Index signature'li obyektga nuqta bilan ham murojaat qilish mumkin: prices.osh. Bu xavfli ko'rinishi mumkin: prices.osh "aniq maydon" kabi o'qiladi, lekin u lug'atdan olinmoqda. Buni taqiqlaydigan bayroq bor:
// @ts-options: {"noPropertyAccessFromIndexSignature": true}
const prices: { [dish: string]: number } = { osh: 35000 };
console.log(prices.osh);Kompilyator:
TS4111: Property 'osh' comes from an index signature, so it must be accessed with ['osh'].Tarjimasi: "osh xususiyati index signature'dan keladi, shuning uchun unga ['osh'] bilan murojaat qilish kerak". Kvadrat qavs o'quvchiga signal beradi: "bu kalit bo'lmasligi mumkin".
Bu bayroq — uslub masalasi. vazifalar loyihasida o'lchanganda u 13 ta joyni ko'rsatdi (xom.versiya kabi). Hammasi to'g'ri ishlaydigan kod edi, shuning uchun bayroq yoqilmadi. Batafsil — Strict bayroqlar oilasi darsida.
Tekshirib ko'ring:
noUncheckedIndexedAccessyoqilgan.const p = prices["osh"] ?? 0;—pning turi qanday?
Javob
number. prices["osh"] — number | undefined, ?? esa undefined bo'lsa o'ng tomonni (0) oladi. Natijada undefined qolmaydi. Bu lug'atdan qiymat olishning eng qisqa xavfsiz yo'li.
4. Record<K, V>
4.1 Index signature'ning qisqa yozuvi
TypeScript'da tayyor tur bor — Record ("yozuv"). Burchak qavs ichida ikki tur: kalit turi va qiymat turi:
const prices: Record<string, number> = { osh: 35000, manti: 30000 };
console.log(prices["manti"]); // 30000Record<string, number> — { [x: string]: number } ning aynan o'zi, faqat qisqaroq. Array<number> kabi bu ham generic yozuv (Massiv va tuple turlari darsida o'qishni o'rgangansiz). Record qanday yasalgani — Utility types va Mapped types darslarida.
4.2 Aniq kalitlar: hammasi majburiy
Record ning haqiqiy kuchi — kalit o'rnida literal union berish. Index signature'da bu taqiqlangan edi (TS1337), Record da esa mumkin:
type DishName = "osh" | "manti" | "lagmon";
const prices: Record<DishName, number> = {
osh: 35000,
manti: 30000,
lagmon: 28000,
};
console.log(prices.lagmon); // 28000Bu endi lug'at emas — uchta aniq maydonli obyekt. Record<DishName, number> — "DishName dagi har bir nomga son". Bitta taomni unutsak:
type DishName = "osh" | "manti" | "lagmon";
const prices: Record<DishName, number> = { osh: 35000, manti: 30000 };Kompilyator:
TS2741: Property 'lagmon' is missing in type '{ osh: number; manti: number; }' but required in type 'Record<DishName, number>'.Tarjimasi: "lagmon xususiyati { osh: number; manti: number; } turida yo'q, lekin Record<DishName, number> turida majburiy". Ya'ni: "uchta taom va'da qilgansiz, ikkitasini berdingiz — lag'mon narxi qani?"
Ortiqcha taom qo'shsak:
type DishName = "osh" | "manti" | "lagmon";
const prices: Record<DishName, number> = {
osh: 35000,
manti: 30000,
lagmon: 28000,
somsa: 8000,
};Kompilyator:
TS2353: Object literal may only specify known properties, and 'somsa' does not exist in type 'Record<DishName, number>'.Tarjimasi: "Obyekt literalida faqat ma'lum xususiyatlar bo'lishi mumkin, somsa esa Record<DishName, number> turida yo'q". Bu Obyekt turlari darsidagi ortiqcha xususiyat tekshiruvi.
Ikkala xato ham juda qimmatli. Ertaga DishName ga "somsa" qo'shilsa, narxlar obyekti darhol TS2741 beradi: "somsa narxini unutdingiz". Union va unga bog'liq lug'at doim bir-biriga mos qoladi.
Indeksdagi farqqa ham qarang. Kalitlar aniq bo'lgani uchun prices.lagmon turi — number, noUncheckedIndexedAccess yoqilgan bo'lsa ham. Kalit bor ekani turda yozilgan, undefined bo'lishi mumkin emas.
4.3 Ba'zi kalitlar bo'lmasligi mumkin
Ombordagi qoldiq: hamma taom ham hisoblanmagan bo'lishi mumkin. Unda kalitlar aniq, lekin har biri ixtiyoriy. Buning uchun Record ni Partial ("qisman") ga o'raymiz:
type DishName = "osh" | "manti" | "lagmon";
const stock: Partial<Record<DishName, number>> = { osh: 12 };
console.log(stock.osh, stock.manti); // 12 undefinedPartial<…> — ichidagi turning har maydonini ixtiyoriy (?) qiladigan tayyor tur. Kompilyator bu yozuvni ichkaridan tashqariga qarab ochadi:
| Qadam | Nima bo'ldi? | Natija |
|---|---|---|
| 1 | DishName ning har nomi kalit bo'ldi |
Record<DishName, number> |
| 2 | Ochilgan ko'rinishi | { osh: number; manti: number; lagmon: number } |
| 3 | Partial har maydonga ? qo'ydi |
{ osh?: number; manti?: number; lagmon?: number } |
Shuning uchun hover'da stock.manti — number | undefined, bayroqsiz ham. Endi tekshiruvsiz songa berib bo'lmaydi:
type DishName = "osh" | "manti" | "lagmon";
const stock: Partial<Record<DishName, number>> = { osh: 12 };
const left: number = stock.manti;Kompilyator:
TS2322: Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.Tarjimasi: "number | undefined turini number ga berib bo'lmaydi. undefined ni number ga berib bo'lmaydi". Ikkinchi qator sababni aniqlaydi: muammo — union'ning undefined qismida. TypeScript aytyapti: "manti hali sanalmagan bo'lishi mumkin".
Bu juftlik bilan uchta holat yopiladi. Record<Union, V> — hamma kalit bor. Partial<Record<Union, V>> — kalitlar ma'lum, lekin bo'lmasligi mumkin. Record<string, V> — kalitlar noma'lum. Partial va boshqa tayyor turlar — Utility types darsida.
4.4 vazifalar dagi Record
vazifalar loyihasida filtrlarning bo'sh holat matnlari aynan shunday yozilgan (render.ts, TypeScript va DOM qadamida):
// Record<Filtr, string>: har filtrga matn — biri unutilsa, tsc aytadi
const boshMatnlar: Readonly<Record<Filtr, string>> = Object.freeze({
hammasi: "Hozircha vazifa yo'q. Birinchisini qo'shing!",
faol: "Faol vazifa yo'q.",
bajarilgan: "Bajarilgan vazifa hali yo'q.",
});Filtr — "hammasi" | "faol" | "bajarilgan". Kimdir yangi filtr qo'shsa, bu obyekt TS2741 bilan "matnini yozing" deydi. Readonly<…> — obyektning hamma maydonini readonly qiladigan tayyor tur (Utility types darsida). Object.freeze esa ishlash paytidagi himoya (Obyektni himoyalash).
Tekshirib ko'ring:
type Size = "S" | "M" | "L"; const extra: Record<Size, number> = { S: 0, M: 3000 };— nima bo'ladi?{ S: 0, M: 3000, L: 5000 }chi?
Javob
Birinchisi — TS2741: Property 'L' is missing…: Record da union'ning har bir a'zosi majburiy kalit. Ikkinchisi — xatosiz: uchala o'lcham bor, ortiqcha kalit yo'q.
5. Obyekt aylanganda kalitlar string
5.1 Object.keys nega string[]?
Kalitlari aniq obyektni aylanib chiqamiz:
const prices = { osh: 35000, manti: 30000 };
const names = Object.keys(prices);
const pairs = Object.entries(prices);
console.log(names, pairs.length); // [ 'osh', 'manti' ] 2Hover'da names: string[], pairs: [string, number][] — ("osh" | "manti")[] emas. Nega TypeScript kalitlarni "unutdi"?
Sababi — Strukturaviy tiplash. { osh: number; manti: number } turiga ko'proq maydonli obyekt ham mos keladi. Funksiya shu turdagi parametrni olsa, haqiqatda unga { osh, manti, somsa } kelishi mumkin. Object.keys esa haqiqiy kalitlarni qaytaradi, "somsa" ni ham. Shuning uchun TypeScript halol javob beradi: "satrlar".
5.2 string kalit bilan aniq Record ga murojaat
Shu sabab aniq kalitli obyektga istalgan satr bilan murojaat qilib bo'lmaydi:
type DishName = "osh" | "manti";
const prices: Record<DishName, number> = { osh: 35000, manti: 30000 };
function priceOf(name: string): number {
return prices[name];
}Kompilyator:
TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<DishName, number>'.
No index signature with a parameter of type 'string' was found on type 'Record<DishName, number>'.Tarjimasi: "string turidagi ifoda bilan Record<DishName, number> ga indeks berib bo'lmaydi, shuning uchun element yashirincha any bo'ldi. Bu turda string parametrli index signature topilmadi". Oddiyroq qilib: "istalgan satr kalit bo'lishi mumkin, lekin bu obyektda faqat ikki kalit bor".
Tuzatish — satrni avval toraytirish. Custom type guard darsidagi tur predikati shu yerda kerak bo'ladi:
type DishName = "osh" | "manti";
const prices: Record<DishName, number> = { osh: 35000, manti: 30000 };
function isDishName(name: string): name is DishName {
return Object.hasOwn(prices, name);
}
function priceOf(name: string): number {
if (!isDishName(name)) {
throw new RangeError(`Menyuda yo'q: ${name}`);
}
return prices[name];
}
console.log(priceOf("manti")); // 30000Object.hasOwn(obyekt, kalit) — "bu kalit obyektning o'zida bormi?" (Object.* metodlari). Nega name in prices emas? Buni keyingi bo'limdagi tuzoq ko'rsatadi.
isDishName true qaytargan shoxda name ning turi string dan DishName ga torayadi. Shuning uchun prices[name] endi xatosiz va natija — number. Tekshiruv ham, tur ham bir joyda: noto'g'ri nom ishlash paytida RangeError beradi, to'g'risi esa tur darajasida tasdiqlanadi.
Tekshirib ko'ring:
const names = Object.keys(prices) as DishName[];— bu qator nimani va'da qiladi va qachon yolg'on bo'lib chiqadi?
Javob
as — "men bilaman, ishon" degani: kalitlar faqat DishName lardan iborat deb va'da beradi. prices haqiqatan shu obyekt bo'lsa — rost. Lekin funksiyaga ortiqcha maydonli obyekt kelsa (strukturaviy tiplash ruxsat beradi), massivda DishName bo'lmagan kalit paydo bo'ladi va tur yolg'on gapiradi. as tekshirmaydi — xavfsiz yo'l tur predikati.
6. Oddiy obyekt lug'atining tuzog'i: meros kalitlari
6.1 "constructor" degan so'z
Kunlik buyurtmalarni sanaymiz: qaysi so'z necha marta. Ro'yxatda tasodifan "constructor" degan yozuv bor (masalan, kimdir izoh maydoniga yozgan):
const words = ["osh", "constructor", "osh"];
const counts: Record<string, number> = {};
for (const w of words) {
counts[w] = (counts[w] ?? 0) + 1;
}
console.log(counts["osh"]);
console.log(counts["constructor"]);Konsolda:
2
function Object() { [native code] }1tsc — xatosiz, hamma tur to'g'ri ko'rinadi. Natija esa g'alati: son o'rniga funksiya matni va "1". Sababi — prototiplar. Oddiy {} obyekt Object.prototype dan meros oladi, unda esa constructor, toString, hasOwnProperty kabi xususiyatlar bor. counts["constructor"] bo'sh obyektda ham undefined emas — meros qolgan funksiya. ?? uni "bor qiymat" deb oldi, unga 1 qo'shilib, satr chiqdi.
TypeScript buni ko'rmaydi: Record<string, number> "har qiymat — son" deydi va unga ishonadi. Bu ham "tur — faqat va'da" holati. name in prices ham shu sababli xavfli: "toString" in prices — true.
6.2 Himoya: prototipsiz obyekt yoki Map
Ikki yechim bor:
Object.create(null)— prototipi yo'q obyekt, unda meros kalitlar yo'q.Map— kalit-qiymat juftliklari uchun maxsus tuzilma (Mapdarsi). Unda meros kalitlar umuman yo'q.
const words = ["osh", "constructor", "osh"];
const counts = new Map<string, number>();
for (const w of words) {
counts.set(w, (counts.get(w) ?? 0) + 1);
}
console.log(counts.get("constructor")); // 1
console.log(counts.get("manti")); // undefinednew Map<string, number>() — bo'sh Map, kalit va qiymat turi burchak qavsda (xuddi Record kabi). Map ning yana bir afzalligi: counts.get(…) turi doim number | undefined, bayroqsiz ham. TypeScript sizni tekshirishga majbur qiladi.
6.3 Qachon nima?
Tanlash uchun ikki savol yetadi:
flowchart TD
A["Kalit → qiymat"] --> B{"Kalitlar ro'yxati<br/>ma'lummi?"}
B -- "ha" --> C["Record<Union, V><br/>hammasi majburiy"]
B -- "yo'q" --> D{"Kalit<br/>foydalanuvchidanmi?"}
D -- "ha" --> E["Map<K, V>"]
D -- "yo'q, JSON" --> F["Record<string, V><br/>+ bayroq"]Record<string, V> |
Map<K, V> |
|
|---|---|---|
| JSON'ga to'g'ridan-to'g'ri | ha | yo'q (Object.fromEntries kerak) |
| Kalit turi | satr (son ham satrga aylanadi) | istalgan: son, obyekt |
Meros kalitlar (constructor) |
bor — tuzoq | yo'q |
get natijasi |
V (bayroqsiz) |
doim V | undefined |
Serverdan JSON kelsa yoki unga yozilsa — Record, noUncheckedIndexedAccess bayrog'i bilan. Kalitlar foydalanuvchidan kelsa yoki tez-tez qo'shilib-o'chirilsa — Map. Aniq ro'yxat bo'lsa — Record<Union, V>.
Tekshirib ko'ring: Mehmonlar o'z ismini yozadigan "kim necha marta keldi" hisoblagichi uchun
Record<string, number>mi yokiMap<string, number>mi?
Javob
Map. Kalit — foydalanuvchi yozgan matn. "constructor" yoki "toString" kabi ism kirib qolishi mumkin va oddiy obyektda u meros xususiyat bilan to'qnashadi. Map da bunday kalitlar yo'q, get esa doim undefined ni tekshirtiradi.
7. interface va index signature
7.1 Yashirin imzo faqat type da
vazifalar loyihasida .ts ga ko'chishning birinchi qadamida (type va interface qadami) haqiqiy xato chiqqan edi. Uning sababi — shu darsning mavzusi. Kichik misolda takrorlaymiz:
interface Guest {
name: string;
people: number;
}
function fieldCount(data: Record<string, unknown>): number {
return Object.keys(data).length;
}
const guest: Guest = { name: "Malika", people: 2 };
fieldCount(guest);Kompilyator:
TS2345: Argument of type 'Guest' is not assignable to parameter of type 'Record<string, unknown>'.
Index signature for type 'string' is missing in type 'Guest'.Tarjimasi: "Guest turidagi argumentni Record<string, unknown> parametriga berib bo'lmaydi: Guest turida string uchun index signature yo'q". Guest ni type bilan yozsangiz, xato yo'qoladi:
type Guest = {
name: string;
people: number;
};
function fieldCount(data: Record<string, unknown>): number {
return Object.keys(data).length;
}
const guest: Guest = { name: "Malika", people: 2 };
console.log(fieldCount(guest)); // 2Nega shunday? TypeScript type bilan yozilgan obyekt turiga yashirin index signature beradi. Uning hamma kaliti ma'lum, demak "har satr kalit — unknown" degan talab bajariladi. Interfeysga esa bermaydi, chunki u declaration merging bilan keyinroq kengayishi mumkin. Unga kelajakda boshqa turdagi maydon qo'shilishi mumkin, shuning uchun TypeScript oldindan va'da bermaydi.
Bu farq faqat Record<string, …> kabi "istalgan kalit" turlariga berishda chiqadi. Oddiy holatlarda — maydonlarni o'qish, funksiyaga berish — type va interface bir xil ishlaydi.
vazifalar dagi xato aynan shu edi: tekshiruv funksiyasi ma'lumotni Record<string, unknown>[] deb qabul qilardi, interface VazifaMalumoti esa unga mos kelmadi. Kanon qarori — turlar.ts dagi ma'lumot shakllari type bilan yoziladi.
8. Ko'p uchraydigan xatolar
8.1 Index signature'da literal union
{ [dish: "osh" | "manti"]: number } — TS1337. Tuzatish: Record<"osh" | "manti", number>.
8.2 Aniq maydon imzo turiga mos emas
{ [k: string]: number; currency: string } — TS2411. Tuzatish: lug'atni alohida maydonga oling (prices: Record<string, number>).
8.3 Lug'atdan olingan qiymatga ko'r-ko'rona ishonish
prices[name] — number, haqiqatda undefined bo'lishi mumkin. Tuzatish: noUncheckedIndexedAccess yoqing, ?? yoki === undefined tekshiruvi.
8.4 Aniq Record ga string bilan murojaat
prices[name] (name: string) — TS7053. Tuzatish: avval tur predikati bilan toraytiring (isDishName). as DishName yozib qo'yish xatoni yashiradi, lekin tekshirmaydi (Type assertion darsidagi xavf).
8.5 Foydalanuvchi kalitlari oddiy obyektda
"constructor", "toString", "__proto__" kabi kalitlar meros xususiyatlar bilan to'qnashadi. Tuzatish: Map yoki Object.create(null), tekshiruvda in o'rniga Object.hasOwn.
9. Mashqlar
1-mashq (oson): Turni ayting
type Table = 1 | 2 | 3;
const waiters: Record<Table, string> = {
1: "Sardor",
2: "Sardor",
3: "Bobur",
};
const notes: Record<string, string> = {};
console.log(Object.keys(waiters).length); // 3waiters da nechta majburiy kalit bor: [:3]. Object.keys(waiters) ning turi — string[] mi yoki Table[] mi: [:string]. noUncheckedIndexedAccess yoqilganda notes["5"] ning turi string | …: .
Yechim
- Uchta:
Record<Table, string>— union'ning har a'zosi majburiy kalit. Son kalitlar ham obyektda satrga aylanadi, lekin tur darajasida1,2,3tekshiriladi. string[]:Object.keysdoim satrlar massivini qaytaradi (strukturaviy tiplash tufayli).string | undefined:notes— istalgan kalitli lug'at, bayroq har murojaatgaundefinedni qo'shadi.
2-mashq (o'rta): Ish vaqti jadvali
Bu kod hafta kunlari uchun ish vaqtini saqlaydi, lekin uchta muammo bor: yakshanba unutilgan, tur buni aytmaydi va mavjud bo'lmagan kun so'ralganda TypeError chiqadi.
const hours: Record<string, string> = {
dushanba: "07:00–23:00",
seshanba: "07:00–23:00",
};
function hoursOf(day: string): string {
return hours[day].toUpperCase();
}
console.log(hoursOf("yakshanba"));Konsolda:
TypeError: Cannot read properties of undefined (reading 'toUpperCase')Vazifa:
type Day = "dushanba" | "seshanba" | "yakshanba"yozing (qisqalik uchun uch kun).hoursniRecord<Day, string>qiling —tscunutilgan kunni ko'rsatsin.hoursOf(day: Day)— endi noto'g'ri kun nomi chaqiruvning o'zida ushlansin.- Yakshanba —
"dam olish".
Yechim
Birinchi qadamdan keyin (yakshanba hali yo'q) tsc shunday deydi: TS2741: Property 'yakshanba' is missing in type '{ dushanba: string; seshanba: string; }' but required in type 'Record<Day, string>'. Tuzatilgan kod:
type Day = "dushanba" | "seshanba" | "yakshanba";
const hours: Record<Day, string> = {
dushanba: "07:00–23:00",
seshanba: "07:00–23:00",
yakshanba: "dam olish",
};
function hoursOf(day: Day): string {
return hours[day];
}
console.log(hoursOf("yakshanba")); // dam olishEndi hoursOf("juma") yozsangiz, tsc darhol to'xtatadi: "juma" Day turida yo'q (TS2345). Xato ishlash paytidan yozish paytiga ko'chdi. hours[day] turi string — noUncheckedIndexedAccess bilan ham: kalit Day dan, demak albatta bor.
3-mashq (qiyin): Amaliy tajriba — buyurtma hisobi
Mashq kurs/mashqlar/15/ papkasida (noUncheckedIndexedAccess yoqilgan). 17-lugat/stock.ts faylini yarating:
type DishName = "osh" | "manti" | "lagmon"vaPRICES: Readonly<Record<DishName, number>>(35 000, 30 000, 28 000).orderTotal(order)— buyurtma: "taom nomi → porsiya soni" lug'ati (Readonly<Record<string, number>>). Menyuda yo'q taom —RangeError. Ishora:Object.entries(order)—[nom, soni]juftliklari.countDishes(names)— kun davomidagi taom nomlari ro'yxatidan "nom → necha marta"Mapqaytarsin.
Avval eng sodda variantni yozing — sum += PRICES[name] * qty; — va npx tsc --pretty false ni ishga tushiring. Keyin testlar: summa (bo'sh buyurtma ham), menyuda yo'q taom ("somsa" va "constructor"), sanash.
Yechim
Sodda variantda tsc 7.0.2 shunday dedi:
17-lugat/stock.ts(14,12): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Readonly<Record<DishName, number>>'.
No index signature with a parameter of type 'string' was found on type 'Readonly<Record<DishName, number>>'.«string kalit bilan aniq Record ga murojaat» bo'limidagi holat: Object.entries kalitni string deb beradi, PRICES esa faqat uch kalitni taniydi. Toraytirish kerak:
// 17-lugat/stock.ts — «Bahor» narxlari va buyurtma hisobi
export type DishName = "osh" | "manti" | "lagmon";
export const PRICES: Readonly<Record<DishName, number>> = {
osh: 35000,
manti: 30000,
lagmon: 28000,
};
// Tur predikati: matn menyudagi taom nomimi?
export function isDishName(name: string): name is DishName {
return Object.hasOwn(PRICES, name);
}
// buyurtma: taom nomi → porsiya soni
export function orderTotal(
order: Readonly<Record<string, number>>,
): number {
let sum = 0;
for (const [name, qty] of Object.entries(order)) {
if (!isDishName(name)) {
throw new RangeError(`Menyuda yo'q: ${name}`);
}
sum += PRICES[name] * qty;
}
return sum;
}
// Kun davomidagi buyurtmalar: qaysi taom necha marta
export function countDishes(
names: readonly string[],
): Map<string, number> {
const counts = new Map<string, number>();
for (const name of names) {
counts.set(name, (counts.get(name) ?? 0) + 1);
}
return counts;
}// 17-lugat/stock.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { orderTotal, countDishes, isDishName } from "./stock.ts";
test("orderTotal: 2 osh va 1 manti", () => {
assert.equal(orderTotal({ osh: 2, manti: 1 }), 100000);
assert.equal(orderTotal({}), 0);
});
test("orderTotal: menyuda yo'q taom — RangeError", () => {
assert.throws(() => orderTotal({ somsa: 1 }), RangeError);
assert.throws(() => orderTotal({ constructor: 1 }), RangeError);
});
test("isDishName: faqat o'z kalitlari", () => {
assert.equal(isDishName("lagmon"), true);
assert.equal(isDishName("toString"), false);
});
test("countDishes: Map bilan sanash", () => {
const counts = countDishes(["osh", "constructor", "osh"]);
assert.equal(counts.get("osh"), 2);
assert.equal(counts.get("constructor"), 1);
assert.equal(counts.get("manti"), undefined);
});Object.entries(order)dagiqty—number,undefinedemas:entriesfaqat mavjud juftliklarni beradi.isDishName("toString")—false, chunkiObject.hasOwnmeros xususiyatlarni hisoblamaydi."toString" in PRICESesatruebo'lardi vaPRICES["toString"] * qty—NaN.countDishes—Map:"constructor"oddiy so'z kabi sanaldi.
npx tsc --pretty false — chiqishsiz. node --test 17-lugat/stock.test.ts bizda (Node 24.21.0) shunday chiqdi:
✔ orderTotal: 2 osh va 1 manti (3.4322ms)
✔ orderTotal: menyuda yo'q taom — RangeError (1.6266ms)
✔ isDishName: faqat o'z kalitlari (0.4582ms)
✔ countDishes: Map bilan sanash (0.7456ms)
ℹ tests 4
ℹ suites 0
ℹ pass 4
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 369.2309XATOLAR.md ga qo'shing: TS1337, TS2411, TS2353, TS7053, TS4111 va interface uchun "Index signature … is missing". Keyin kurs/mashqlar papkasidan:
git add 15/17-lugat 15/XATOLAR.md
git commit -m "15/17: stock.ts — Record, tur predikati va Map"10. Real ishda
- Tarjimalar (i18n):
Record<Lang, string>yokiRecord<string, string>— "kalit → matn". Aniq tillar ro'yxati bilanRecord<"uz" | "ru" | "en", string>har tilni unutmaslikni kafolatlaydi. - Holat → matn/rang jadvallari:
vazifalardagiboshMatnlar,PANEL_RANGLARIkabi — enum vaswitcho'rniga ko'p ishlatiladi. Keyingi darslarda yana ko'rasiz. - HTTP sarlavhalari:
headers: Record<string, string>—vazifalardagiapi.tsda ham shunday. - Kesh va hisoblagichlar —
Map: kalitlar dinamik, tez-tez qo'shiladi va o'chiriladi. - Intervyu: "Index signature nima?", "
RecordvaMapfarqi?", "Object.keysnegastring[]qaytaradi?" — tez-tez so'raladi.
Xulosa
- Index signature
{ [k: string]: V }— kalitlari noma'lum lug'at. Kalit turi —string,number,symbolyoki shablon; aniq maydonlar imzo turiga mos bo'lishi kerak. Record<string, V>— o'sha narsaning qisqa yozuvi.Record<Union, V>esa union'ning har a'zosini majburiy kalit qiladi.- Lug'atdan olingan qiymat turi
undefinedni hisobga olmaydi.noUncheckedIndexedAccessbuni tuzatadi,??va tekshiruv bilan ishlang. Object.keys/entrieskalitlarnistringdeb beradi. AniqRecordga murojaatdan oldin satrni tur predikati bilan toraytiring.- Oddiy obyekt lug'atida meros kalitlar (
constructor) bor. Foydalanuvchi kalitlari uchunMapyokiObject.hasOwn. interfacega yashirin index signature berilmaydi —Record<string, unknown>ga mos kelishi kerak bo'lsa,typeyozing.
Keyingi dars: Klasslar TypeScript'da — public/private/protected, readonly, abstrakt klass va implements. vazifalar dagi Vazifa klassi .ts ga ko'chadi.
Manbalar
- TypeScript Handbook: "Object Types" (Index Signatures) — typescriptlang.org/docs/handbook/2/objects.html#index-signatures
- TypeScript Handbook: "Utility Types" (
Record<Keys, Type>) — typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type - TSConfig:
noUncheckedIndexedAccess,noPropertyAccessFromIndexSignature— typescriptlang.org/tsconfig - MDN:
Object.hasOwn(),Map— developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!