Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. infer — qolipga solib tutib olish
- 2.1 Destructuring bilan solishtiramiz
- 2.2 Kompilyator qanday hisoblaydi
- 3. ReturnType va Parameters ni o'zimiz yozamiz
- 3.1 Funksiya natijasi
- 3.2 Funksiya parametrlari
- 3.3 Ikki odatiy xato
- 4. Promise ichiga kirish
- 4.1 O'z Unwrap imiz
- 4.2 Tayyor Awaited va [number]
- 5. Qolip ichida cheklov: infer E extends …
- 6. Nozik holatlar — kutubxona muallifi darajasi
- 6.1 Overload'li funksiya — oxirgi imzo
- 6.2 Bitta nom ikki joyda
- 7. TypeScript ushlamaydigan tuzoq: any qaytaradigan funksiya
- 8. infer qachon kerak — va qachon kerak emas
- 9. Ko'p uchraydigan xatolar
- 9.1 infer ni shartdan tashqarida yozish
- 9.2 Tutilgan nomni : shoxida ishlatish
- 9.3 typeof ni unutish
- 9.4 async funksiyada Awaited ni unutish
- 9.5 Jim never
- 10. Mashqlar
- 1-mashq (oson): Tutilgan turni ayting
- 2-mashq (o'rta): Last va FirstArg
- 3-mashq (qiyin): Amaliy tajriba — API turlarini chiqarish
- 11. Real ishda
- Xulosa
- Manbalar
TypeScript infer: conditional type ichida turni "tutib olish"
Qisqacha:
inferkalit so'zi — conditional type ichida "shu joydagi turni tutib ol va unga nom ber" degan buyruq. Masalan,T extends Promise<infer V> ? V : T— "TPromise<…>shaklida bo'lsa, ichidagi turniVdeb nomla va uni qaytar". TayyorReturnType,Parameters,Awaitedaynan shunday yozilgan. Muhim cheklov:inferfaqatextendsdan keyin, shart ichida ishlaydi.
Bu darsda
inferni destructuring bilan solishtirib, "qolipga solish" sifatida tushuntira olasiz.ReturnTypevaParametersni o'zingiz yozasiz valib.es5.d.tsdagi asli bilan solishtirasiz.- Promise ichidagi va massiv elementi turini chiqarasiz:
Awaited<ReturnType<typeof f>>[number]. infer E extends stringbilan tutilgan turga cheklov qo'yasiz.inferushlamaydigan tuzoqni —anyqaytaradigan funksiyani — taniysiz.
Oldin bilishingiz kerak: Conditional types, Utility types, Massiv destructuring.
1. Nega bu kerak?
O'tgan darsda conditional type bilan "ha yoki yo'q" savollarini berdik: "bu tur satrmi?", "bu holat yopiqmi?". Lekin ba'zan savol boshqacha: "bu turning ichida nima bor?". Masalan, "bu Promise nima va'da qilyapti?" yoki "bu massiv nimalardan iborat?".
«Bahor» admin panelida menyu API'si bor. Uni boshqa modul yozgan, ichidagi taom turi esa eksport qilinmagan:
type Dish = { id: number; name: string; price: number };
const menuApi = {
async list(): Promise<Dish[]> {
return [{ id: 1, name: "Osh", price: 35000 }];
},
};
const menu = await menuApi.list();
console.log(menu[0]?.name); // OshSardor menyu jadvalini chizadigan funksiya yozyapti. Uning parametri — "bitta taom qatori". Dish ni import qilib bo'lmaydi, u yopiq. Qo'lda yozsa — { id: number; name: string; price: number } — mapped types darsidagi takrorlanish muammosi qaytadi: API'ga spicy qo'shilsa, jadval turi eskiradi.
Tur aslida bor — menuApi.list ning imzosida: Promise<Dish[]>. Bizga uning ichidan, ikki qavat chuqurlikdagi Dish kerak. Utility types darsidagi ReturnType bitta qavatni ochadi:
type Dish = { id: number; name: string; price: number };
async function fetchMenu(): Promise<Dish[]> {
return [{ id: 1, name: "Osh", price: 35000 }];
}
type Menu = ReturnType<typeof fetchMenu>;
const menu: Menu = await fetchMenu();
const first: Menu[number] = { id: 1, name: "Osh", price: 35000 };Kompilyator:
TS2739: Type 'Dish[]' is missing the following properties from type 'Promise<Dish[]>': then, catch, [Symbol.toStringTag], finally
TS2537: Type 'Promise<Dish[]>' has no matching index signature for type 'number'.- Birinchisi: "
Dish[]turidaPromise<Dish[]>ningthen,catch… xususiyatlari yo'q".ReturnType—Promise<Dish[]>,awaitesa massivning o'zini beradi. - Ikkinchisi: "
Promise<Dish[]>danumberindeksi yo'q" — Promise massiv emas,[number]bilan element olib bo'lmaydi.
Promise ichiga "kirish" kerak. Buni infer qiladi. Darsning oxirida Awaited<ReturnType<typeof fetchMenu>>[number] ni o'qiy olasiz va undagi har bir qadamni o'zingiz yoza olasiz.
2. infer — qolipga solib tutib olish
2.1 Destructuring bilan solishtiramiz
Massiv destructuring ni eslang:
const order = ["osh", 2] as const;
const [dish, count] = order;
console.log(dish, count); // osh 2Chap tomon — qolip: "ikki elementli massiv, birinchisini dish deb, ikkinchisini count deb ol". JavaScript qiymatni qolipga soladi va bo'sh joylarga nom beradi.
Destructuring'da ikki muhim narsa bor. Birinchisi — qolip shakli: [a, b] faqat massivga mos, { name } esa obyektga. Ikkinchisi — nomlar: qolipdagi bo'sh joylarga qiymat tushadi va ular keyingi kodda ishlatiladi.
infer — xuddi shu narsa, faqat turlar bilan:
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
const dish: ElementOf<string[]> = "osh";
const price: ElementOf<number[]> = 35000;
console.log(dish, price); // osh 35000T extends readonly (infer E)[] — "T ni "nimadir-massiv" qolipiga sol; o'sha "nimadir" ni E deb nomla". Mos kelsa — ? dan keyingi qism E ni ishlata oladi.
| Bo'lak | Ma'nosi |
|---|---|
T extends … |
shart — o'tgan darsdagi kabi |
readonly (…)[] |
qolip: massiv (oddiy ham, readonly ham) |
infer E |
"bu joydagi turni E deb ol" |
? E : never |
mos kelsa — E, aks holda — never |
infer (inglizcha "xulosa chiqarmoq") — kompilyatorga "bu turni men aytmayman, o'zing qolipdan chiqar" degani. Kunlik o'xshatish: kalit nusxasini olish. Kalitni mum ustiga bosasiz — mum kalitning shaklini "tutib oladi". Qolip — mum, infer E — kalitning izi.
2.2 Kompilyator qanday hisoblaydi
ElementOf<string[]>:
| Qadam | Nima qiladi | Natija |
|---|---|---|
| 1 | T o'rniga string[] |
string[] extends readonly (infer E)[] |
| 2 | string[] ni qolipga solishga urinadi |
(infer E)[] ↔ string[] |
| 3 | E o'rniga nima tursa mos keladi? |
E = string |
| 4 | shart bajarildi — ? shoxi |
string |
Mos kelmasa-chi? ElementOf<string>:
| Qadam | Nima qiladi | Natija |
|---|---|---|
| 1 | string extends readonly (infer E)[] |
string — massiv emas |
| 2 | qolipga tushmadi — E aniqlanmaydi |
— |
| 3 | : shoxi |
never |
Bu destructuring'dan farq qiladi. JavaScript'da const [a] = 35000 — TypeError (son iterable emas). Tur darajasida esa qolipga tushmaslik xato emas — bu shunchaki shartning "yo'q" javobi, va : shoxi ishlaydi. Shuning uchun : shoxiga nima yozishni o'zingiz tanlaysiz: never ("bunday bo'lmasligi kerak") yoki T ning o'zi ("o'zgartirmasdan qaytar").
never — "hech qanday qiymat bo'la olmaydi". U jim keladi, xato bermaydi. Xato faqat uni ishlatganda chiqadi:
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
const dish: ElementOf<string> = "osh";Kompilyator:
TS2322: Type '"osh"' is not assignable to type 'never'.""osh" ni never ga berib bo'lmaydi". Xabarda never ni ko'rsangiz — ko'pincha qaysidir infer qolipga tushmagan. Qaysi biri ekanini hover bilan, ichkaridan tashqariga qarab izlang.
Tekshirib ko'ring:
ElementOf<readonly ["osh", "manti"]>nima beradi?
Javob
"osh" | "manti". Tuple ham massiv — u readonly (infer E)[] qolipiga tushadi. E — barcha elementlar turlarining union'i. Qolipdagi readonly muhim: usiz readonly tuple (as const natijasi) qolipga tushmasdi va natija never bo'lardi.
3. ReturnType va Parameters ni o'zimiz yozamiz
3.1 Funksiya natijasi
Funksiya turi qolipi — (...args: …) => …. Natija joyiga infer R qo'yamiz:
type MyReturn<T> = T extends (...args: any[]) => infer R ? R : never;
function totalPrice(prices: number[]): number {
return prices.reduce((sum, p) => sum + p, 0);
}
const sum: MyReturn<typeof totalPrice> = totalPrice([35000, 5000]);
console.log(sum); // 40000| Qadam | Nima qiladi | Natija |
|---|---|---|
| 1 | typeof totalPrice |
(prices: number[]) => number |
| 2 | qolip: (...args: any[]) => infer R |
parametrlar — istalgan, natija — R |
| 3 | R ni tutib oladi |
R = number |
| 4 | ? shoxi |
number |
typeof totalPrice — typeof operatori: qiymatdan (funksiyadan) uning turini oladi. (...args: any[]) — "parametrlar qanday bo'lsa ham". Bu yerda any — standart kutubxonadagi kabi ataylab: biz parametrlarni tekshirmayapmiz, faqat qolipning "shu yerida nimadir bor" qismi.
lib.es5.d.ts dagi asli deyarli bir xil:
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any;Farqi — cheklov: T extends (...args: any) => any. U funksiya bo'lmagan narsani kirishdayoq rad etadi. Shu sababli ReturnType<string> — never emas, aniq xato.
3.2 Funksiya parametrlari
infer qolipning istalgan joyida turishi mumkin — natijada ham, parametrlarda ham:
type MyParams<T> = T extends (...args: infer A) => unknown
? A
: never;
function addOrder(table: number, dishes: string[], note?: string) {
return { table, count: dishes.length, note };
}
type OrderArgs = MyParams<typeof addOrder>;
const args: OrderArgs = [5, ["osh", "manti"]];
console.log(addOrder(...args).count); // 2OrderArgs ustida hover:
type OrderArgs = [table: number, dishes: string[], note?: string | undefined]Parametrlar ro'yxati nomlangan tuple bo'lib chiqdi: nomlar ham, ixtiyoriy note? ham saqlangan. Bu tasodif emas. JavaScript'da ham funksiya parametrlari aslida tartiblangan ro'yxat — (...args) rest parametri ularni massivga yig'adi. TypeScript bu ro'yxatni tuple turi bilan ifodalaydi, infer A esa uni butunligicha tutadi.
Qayerda kerak? Masalan, «Bahor» buyurtma tizimida addOrder chaqiruvlarini navbatga qo'yish kerak: internet uzilsa, chaqiruv argumentlari saqlanadi va keyin qayta yuboriladi. Navbat elementi turi — aynan OrderArgs. addOrder ga yangi parametr qo'shilsa, navbat turi o'zi yangilanadi. Uni spread bilan funksiyaga qaytarib berish mumkin — addOrder(...args). Bitta parametr kerak bo'lsa — indeks bilan: MyParams<typeof addOrder>[0] — number.
3.3 Ikki odatiy xato
type Dish = { name: string; price: number };
async function fetchMenu(): Promise<Dish[]> {
return [{ name: "Osh", price: 35000 }];
}
type A = ReturnType<fetchMenu>;
type B = ReturnType<string>;Kompilyator:
TS2749: 'fetchMenu' refers to a value, but is being used as a type here. Did you mean 'typeof fetchMenu'?
TS2344: Type 'string' does not satisfy the constraint '(...args: any) => any'.TS2749: "fetchMenu— qiymat, lekin u tur sifatida ishlatilgan.typeof fetchMenuni nazarda tutdingizmi?". Burchak qavs ichiga faqat tur yoziladi. Funksiya — qiymat, uning turi —typeof fetchMenu.tsctuzatishni o'zi taklif qilyapti.TS2344: "stringturi(...args: any) => anycheklovini qanoatlantirmaydi". Bu —lib.es5.d.tsdagi cheklov ishi.
Tekshirib ko'ring:
MyReturn<typeof addOrder>nima? (addOrderyuqorida.)
Javob
{ table: number; count: number; note: string | undefined }. Funksiya natija turi yozilmagan — TypeScript uni return dagi obyektdan chiqargan. infer R aynan shu chiqarilgan turni tutib oladi. Ya'ni MyReturn natija turini yozmagan funksiyalarda ham ishlaydi.
4. Promise ichiga kirish
4.1 O'z Unwrap imiz
Endi «Nega bu kerak?» bo'limidagi muammo. Bizga Promise ichidagi tur kerak. Qolipni yozamiz: "T — nimadirning Promise'i; o'sha nimadirni V deb ol". Ya'ni qolip — Promise<infer V>:
type Unwrap<T> = T extends Promise<infer V> ? V : T;
const a: Unwrap<Promise<number>> = 35000;
const b: Unwrap<string> = "osh";
console.log(a, b); // 35000 oshE'tibor bering: mos kelmasa — never emas, T ning o'zi. "Promise bo'lsa — ichini ber, bo'lmasa — o'zini". Bu await ning xulqi bilan bir xil: await 35000 ham 35000 beradi.
Endi uch qatlamni birma-bir ochamiz:
type Dish = { id: number; name: string; price: number };
async function fetchMenu(): Promise<Dish[]> {
return [{ id: 1, name: "Osh", price: 35000 }];
}
type Unwrap<T> = T extends Promise<infer V> ? V : T;
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
type Row = ElementOf<Unwrap<ReturnType<typeof fetchMenu>>>;
const row: Row = { id: 2, name: "Manti", price: 30000 };
console.log(row.name); // MantiKompilyator ichkaridan tashqariga hisoblaydi:
| Qadam | Hisob | Natija |
|---|---|---|
| 1 | typeof fetchMenu |
() => Promise<Dish[]> |
| 2 | ReturnType<…> — infer R |
Promise<Dish[]> |
| 3 | Unwrap<…> — infer V |
Dish[] |
| 4 | ElementOf<…> — infer E |
Dish |
Rasmda — har qolip bitta qavatni ochadi:
flowchart LR
A["typeof fetchMenu"] --> B["ReturnType"]
B --> C["Promise of Dish[]"]
C --> D["Unwrap: infer V"]
D --> E["Dish[]"]
E --> F["ElementOf: infer E"]
F --> G["Dish"]Dish eksport qilinmagan bo'lsa ham, uning shakli endi bizda. API'ga yangi maydon qo'shilsa, Row ham o'zi yangilanadi.
Tekshirib ko'ring:
menuApi.findning natijasi —Promise<Dish | undefined>.Unwrap<ReturnType<typeof menuApi.find>>nima beradi? UndanElementOfolsa-chi?
Javob
Unwrap — Dish | undefined: Promise ochildi, ichidagi union o'zgarmadi. ElementOf<Dish | undefined> esa never: union taqsimlanadi (Conditional types), Dish ham, undefined ham massiv emas — ikkalasi never. Massiv qaytarmaydigan funksiyadan element so'rash — jim never ning tipik manbai.
4.2 Tayyor Awaited va [number]
Amalda o'z Unwrap ingizni yozish shart emas — TypeScript 4.5 dan beri tayyor Awaited bor (Utility types). Elementni esa [number] (indexed access) beradi:
type Dish = { id: number; name: string; price: number };
async function fetchMenu(): Promise<Dish[]> {
return [{ id: 1, name: "Osh", price: 35000 }];
}
type Row = Awaited<ReturnType<typeof fetchMenu>>[number];
const row: Row = { id: 3, name: "Somsa", price: 8000 };
console.log(row.price); // 8000Awaited bizning Unwrap dan bitta farqi bilan kuchliroq — ichma-ich Promise'larni ham oxirigacha ochadi:
| Tur | Unwrap |
Awaited |
|---|---|---|
Promise<number> |
number |
number |
Promise<Promise<number>> |
Promise<number> |
number |
string |
string |
string |
Ichma-ich Promise amalda qayerdan keladi? Masalan, async funksiya ichida boshqa Promise'ni await siz qaytarsangiz — JavaScript uni baribir ochadi, Unwrap esa yo'q. Shuning uchun Awaited — await ning aniq tur egizagi, bizning Unwrap esa uning soddalashtirilgan o'quv nusxasi.
Awaited o'z-o'zini chaqiradi — rekursiv tur. Bunday turlarni Rekursiv turlar darsida yozamiz. await ham JavaScript'da xuddi shunday ishlaydi: Promise ichidagi Promise'ni ham kutadi.
5. Qolip ichida cheklov: infer E extends …
Ba'zan tutilgan tur ma'lum shaklda bo'lishi kerak. TypeScript 4.7 dan beri infer ga cheklov qo'shsa bo'ladi:
type TextItems<T> = T extends readonly (infer E extends string)[]
? E
: never;
const dish: TextItems<("osh" | "manti")[]> = "manti";
console.log(dish); // mantiinfer E extends string — "elementni tut, lekin u satr bo'lsagina mos deb hisobla". Bu yerdagi extends — generic cheklov dagi bilan bir xil ma'noda: "kamida shu turga mos bo'lsin". Bu ikki ichma-ich shartning qisqa yozuvi — "massivmi?" va "elementi satrmi?". Ikki kirishni yonma-yon hisoblaymiz:
| Qadam | ("osh" | "manti")[] |
("osh" | 1)[] |
|---|---|---|
| 1. Massiv qolipiga tushdimi? | ha | ha |
2. Tutilgan E |
"osh" | "manti" |
"osh" | 1 |
3. E extends string? |
ha | yo'q — 1 satr emas |
| 4. Natija | "osh" | "manti" |
never |
Tuple'ning birinchi elementi — rest bilan qolip:
type First<T> = T extends readonly [infer F, ...unknown[]]
? F
: never;
const open: First<[open: number, close: number]> = 7;
console.log(open); // 7[infer F, ...unknown[]] — "birinchi elementni F deb ol, qolgani — nima bo'lsa ham". Bo'sh tuple [] da birinchi element yo'q — First<[]> — never. Bu destructuring'dagi const [first] = [] ga o'xshaydi: u yerda undefined, bu yerda never.
Tekshirib ko'ring:
First<["osh", 35000]>vaFirst<string[]>nima beradi?
Javob
First<["osh", 35000]> — "osh": birinchi o'rinda aniq satr turi turibdi. First<string[]> esa never. Oddiy massivda "birinchi element albatta bor" degan kafolat yo'q — u bo'sh bo'lishi mumkin. Qolip [infer F, ...unknown[]] esa kamida bitta elementni talab qiladi, shuning uchun string[] unga tushmaydi.
6. Nozik holatlar — kutubxona muallifi darajasi
Quyidagilar ilova kodida kam uchraydi. Ularni kutubxona turlarini o'qiganda yoki g'alati natijani tushuntirishda bilish foydali. Birinchi o'qishda tushunmasangiz — qo'rqmang, keyin qaytasiz.
6.1 Overload'li funksiya — oxirgi imzo
function format(x: number): string;
function format(x: string): number;
function format(x: number | string): string | number {
return typeof x === "number" ? String(x) : x.length;
}
const r: ReturnType<typeof format> = 3;
console.log(r); // 3format ning ikki overload imzosi bor: son bersangiz — satr, satr bersangiz — son. ReturnType esa faqat oxirgisini oldi: number. Kutilgan javob string | number yoki birinchi imzo (string) bo'lishi mumkin edi — lekin unday emas. infer overload'lar ichidan tanlay olmaydi — u oxirgi imzoni qolipga soladi. Agar sizga birinchi imzo natijasi kerak bo'lsa, ReturnType bermaydi — turni qo'lda yozing.
6.2 Bitta nom ikki joyda
type Both<T> = T extends { a: infer U; b: infer U } ? U : never;
const v: Both<{ a: string; b: number }> = 35000;
console.log(v); // 35000Bir xil U ikki joyda tutildi — natija ikkalasining union'i: string | number. Kompilyator ikki nomzodni topdi va ikkalasini ham sig'diradigan eng tor turni tanladi. (Parametr o'rnida tutilsa — union emas, kesishma bo'ladi. Nega shunday — Branded types va variance darsida.)
7. TypeScript ushlamaydigan tuzoq: any qaytaradigan funksiya
infer funksiyada nima yozilgan bo'lsa, shuni tutadi. Funksiya any qaytarsa — tutilgani ham any:
const saved = '{"name":"Osh","price":35000}';
function loadItem() {
return JSON.parse(saved);
}
type Item = ReturnType<typeof loadItem>;
const item: Item = loadItem();
console.log(item.prise.toFixed(0));tsc — xatosiz. Node esa:
TypeError: Cannot read properties of undefined (reading 'toFixed')JSON.parse natijasi — any (Tur annotatsiyasi darsidagi tuzoq). loadItem natija turi yozilmagan — u ham any. ReturnType esa any ni tutib oldi va Item — any. Shundan keyin item.prise (xato yozilgan price) ham, .toFixed ham tekshirilmaydi. Tur chiqarish zanjiri any ni oxirigacha jim tashib boradi.
Bu xavf infer ning tabiatidan kelib chiqadi. U yangi ma'lumot yaratmaydi — faqat bor turni boshqa joyga ko'chiradi. Manba aniq bo'lsa (Promise<Dish[]>), ko'chirilgani ham aniq. Manba any bo'lsa, any ko'chadi va u tegib o'tgan hamma narsa tekshiruvsiz qoladi. Hover'da type Item = any ni ko'rsangiz — bu ogohlantirish belgisi.
Qoida: ReturnType va infer — faqat funksiya imzosidagi tur qadar ishonchli. Tashqi ma'lumot (JSON.parse, fetch javobi) qaytaradigan funksiyaga natija turini o'zingiz yozing: unknown va keyin tekshiruv (API ma'lumotlarini tiplash darsida to'liq).
8. infer qachon kerak — va qachon kerak emas
infer — kuchli asbob, lekin u "turni boshqa joydan qidirib topish". Ba'zan eng yaxshi yechim — qidirmaslik.
Tur sizning qo'lingizda bo'lsa — uni eksport qiling. menuApi ni o'zingiz yozgan bo'lsangiz, export type Dish = … bitta qator. Kodni o'qigan odam Dish ni darhol tushunadi, Awaited<ReturnType<typeof menuApi.list>>[number] ni esa ochib o'qishi kerak. Ikkalasi bir xil tur, lekin birinchisi tushunarliroq.
Tur yopiq bo'lsa — infer (yoki tayyor ReturnType/Parameters/Awaited). Boshqa jamoaning moduli, eksport qilinmagan kutubxona turi, ichki funksiya natijasi — bu infer ning asosiy ishi. Shunda ham chiqarilgan turga nom bering: type MenuRow = … bitta joyda, keyin hamma joyda MenuRow. Uzun zanjirni har faylda takrorlamang.
Funksiya imzosi any bo'lsa — infer yordam bermaydi. «TypeScript ushlamaydigan tuzoq» bo'limidagi holat: tutiladigan narsa yo'q. Bu yerda turni o'zingiz yozasiz va qiymatni tekshirasiz.
Oddiy qoida: o'z kodingizda infer ni kam yozasiz, kutubxona turlarida esa ko'p o'qiysiz. Ikkalasi uchun ham bugungi qolip tushunchasi yetadi.
9. Ko'p uchraydigan xatolar
9.1 infer ni shartdan tashqarida yozish
type ResultOf<T> = infer R;Kompilyator:
TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type."infer faqat conditional type'ning extends qismida ruxsat etilgan". infer — qolipning bir bo'lagi, qolipsiz u ma'nosiz. Tuzatish: T extends … infer R … ? R : ….
9.2 Tutilgan nomni : shoxida ishlatish
type Unwrap<T> = T extends Promise<infer V> ? V : V;Kompilyator:
TS2304: Cannot find name 'V'."V nomi topilmadi". V faqat qolip mos kelgan shoxda (? dan keyin) mavjud. Mos kelmagan shoxda tutiladigan narsa yo'q edi. Tuzatish: : dan keyin T yoki never.
9.3 typeof ni unutish
ReturnType<fetchMenu> — TS2749. Tuzatish: ReturnType<typeof fetchMenu>.
9.4 async funksiyada Awaited ni unutish
«Nega bu kerak?» bo'limidagi TS2739 va TS2537. async funksiyaning ReturnType i — doim Promise<…>. Tuzatish: Awaited<ReturnType<typeof f>>.
9.5 Jim never
ElementOf<string> — xato emas, never. Xato keyinroq, boshqa joyda chiqadi. Qoida: xabarda kutilmagan never — ichkaridagi infer lardan biri mos kelmagan.
10. Mashqlar
1-mashq (oson): Tutilgan turni ayting
type Unwrap<T> = T extends Promise<infer V> ? V : T;
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
const a: Unwrap<Promise<boolean>> = true;
const b: ElementOf<number[]> = 5000;
const c: Unwrap<number> = 28000;
console.log(a, b, c); // true 5000 28000Unwrap<Promise<boolean>> — [:boolean]. ElementOf<number[]> — [:number]. ElementOf<boolean> — .
Yechim
Promise<boolean> qolipga tushadi, V = boolean. number[] — massiv, E = number. boolean massiv emas — : shoxi, never. Unwrap<number> esa number: Unwrap ning : shoxi never emas, T ning o'zi.
2-mashq (o'rta): Last va FirstArg
Last<T>— tuple'ning oxirgi elementi. Ishora: rest elementni boshiga qo'ying —[...unknown[], infer L].FirstArg<F>— funksiyaning birinchi parametri turi. Ishora: parametrlar qolipi —(a: infer A, ...rest: any[]) => unknown.
Yechim
type Last<T> = T extends readonly [...unknown[], infer L] ? L : never;
type FirstArg<F> = F extends (a: infer A, ...rest: any[]) => unknown
? A
: never;
function bookTable(guest: string, people: number): string {
return `${guest}: ${people} kishi`;
}
const close: Last<[open: number, close: number]> = 23;
const guest: FirstArg<typeof bookTable> = "Dilshod aka";
console.log(close, bookTable(guest, 4)); // 23 Dilshod aka: 4 kishi[...unknown[], infer L] — "boshida nechta bo'lsa ham, oxirgisini L deb ol" (rest element boshida). FirstArg — MyParams<F>[0] bilan bir xil natija beradi, lekin to'g'ridan-to'g'ri qolip bilan.
3-mashq (qiyin): Amaliy tajriba — API turlarini chiqarish
kurs/mashqlar/15/29-infer/menu-api.ts faylini yarating. Unda xotiradagi menuApi bor (Dish turi eksport qilinmaydi):
// 29-infer/menu-api.ts — «Bahor» menyu API'si (xotirada)
type Dish = { id: number; name: string; price: number };
const dishes: Dish[] = [
{ id: 1, name: "Osh", price: 35000 },
{ id: 2, name: "Lag'mon", price: 28000 },
];
export const menuApi = {
async list(): Promise<Dish[]> {
return dishes.map((dish) => ({ ...dish }));
},
async find(id: number): Promise<Dish | undefined> {
return dishes.find((dish) => dish.id === id);
},
async add(name: string, price: number): Promise<Dish> {
const dish = { id: dishes.length + 1, name, price };
dishes.push(dish);
return dish;
},
};Shu faylga qo'shing:
ResultOf<F>— async funksiya va'da qilgan tur (Promise ichidagisi).ArgsOf<F>— parametrlar tuple'i.ElementOf<T>— massiv elementi.MenuRow— bitta taom qatori,menuApi.listdan chiqarilgan;AddArgs—menuApi.addparametrlari.addMany(rows)—AddArgslar ro'yxatini birma-bir qo'shib, qo'shilgan taomlarni qaytaradi. Ishora:menuApi.add(...row).
Avval MenuRow ni ElementOf<ReturnType<typeof menuApi.list>> deb yozing (Promise'ni ochmasdan), testlarni yozing va npx tsc --pretty false ni ishga tushiring. Testlar: list uzunligi va birinchi nomi, yo'q id — undefined, addMany yangi id lar.
Yechim
Promise ochilmagan variantda MenuRow — never bo'lib qoldi va xatolar ishlatilgan joylarda chiqdi:
29-infer/menu-api.test.ts(8,9): error TS2322: Type 'Dish[]' is not assignable to type 'never[]'.
Type 'Dish' is not assignable to type 'never'.
29-infer/menu-api.test.ts(10,25): error TS2339: Property 'name' does not exist on type 'never'.
29-infer/menu-api.test.ts(24,30): error TS2339: Property 'id' does not exist on type 'never'.
29-infer/menu-api.ts(40,16): error TS2345: Argument of type 'Dish' is not assignable to parameter of type 'never'.To'rt xato, to'rttasi ham MenuRow ta'rifidan uzoqda. «Jim never» qoidasi: ReturnType — Promise<Dish[]>, u ElementOf qolipiga tushmadi.
// 29-infer/menu-api.ts — davomi (menuApi dan keyin)
// Funksiya va'da qilgan natija (Promise ichidagi tur)
export type ResultOf<F> =
F extends (...args: any[]) => Promise<infer R> ? R : never;
// Funksiya parametrlari — tuple
export type ArgsOf<F> =
F extends (...args: infer A) => unknown ? A : never;
// Massiv elementi
export type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
export type MenuRow = ElementOf<ResultOf<typeof menuApi.list>>;
export type AddArgs = ArgsOf<typeof menuApi.add>;
export async function addMany(
rows: readonly AddArgs[],
): Promise<MenuRow[]> {
const added: MenuRow[] = [];
for (const row of rows) {
added.push(await menuApi.add(...row));
}
return added;
}ResultOf — ReturnType va Unwrap ning birlashmasi: qolipda natija joyi — Promise<infer R>. AddArgs — [name: string, price: number]. typeof menuApi.list — obyekt ichidagi metodning turi ham typeof bilan olinadi.
// 29-infer/menu-api.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { menuApi, addMany } from "./menu-api.ts";
import type { MenuRow, AddArgs } from "./menu-api.ts";
test("list: menyu nusxasi", async () => {
const menu: MenuRow[] = await menuApi.list();
assert.equal(menu.length, 2);
assert.equal(menu[0]?.name, "Osh");
});
test("find: yo'q taom — undefined", async () => {
assert.equal(await menuApi.find(99), undefined);
});
test("addMany: tuple'lar ro'yxatidan qo'shish", async () => {
const rows: AddArgs[] = [
["Manti", 30000],
["Ko'k choy", 5000],
];
const added = await addMany(rows);
assert.deepEqual(
added.map((dish) => dish.id),
[3, 4],
);
assert.equal((await menuApi.list()).length, 4);
});menu[0]?.name — noUncheckedIndexedAccess yoqilgan: menu[0] — MenuRow | undefined. Tuzatilgach npx tsc --pretty false — chiqishsiz. node --test 29-infer/menu-api.test.ts bizda (Node 24.21.0):
✔ list: menyu nusxasi (1.6845ms)
✔ find: yo'q taom — undefined (0.348ms)
✔ addMany: tuple'lar ro'yxatidan qo'shish (1.7382ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 253.3839XATOLAR.md ga qo'shing: TS1338 (infer shartdan tashqarida), TS2749 (typeof unutilgan), TS2344 (cheklov), TS2537 (Promise'ga [number]) va "kutilmagan never — infer mos kelmagan". Keyin kurs/mashqlar papkasidan:
git add 15/29-infer 15/XATOLAR.md
git commit -m "15/29: menu-api.ts — infer bilan API turlari"11. Real ishda
- Yopiq turlarga kirish. Kutubxona yoki boshqa jamoa moduli turni eksport qilmagan bo'lsa —
ReturnType,Parameters,Awaitedva[number]bilan uni funksiya imzosidan chiqarasiz. Bu juda ko'p uchraydi. vazifalarda:export type ApiKlient = ReturnType<typeof apiKlient>;(api.ts, Utility types qadami) — klient obyektining turi uni yasagan funksiyadan olinadi. Endi siz bu qatorning ichidainfer Rturganini bilasiz.- Kutubxona muallifi darajasi: Zod'ning
z.infer<typeof schema>(Zod darsida) — nomi bejizinferemas: sxema obyektidan TypeScript turi aynan shunday qoliplar bilan chiqariladi. React'daComponentProps<typeof Button>— komponent parametrlaridan tur (17-qismdan keyin). - Intervyu: "
ReturnTypeni o'zingiz yozing", "infernima?", "Awaitedqanday ishlaydi?" — TypeScript intervyusining klassik savollari.
Xulosa
infer X— conditional type qolipidagi bo'sh joy: "bu yerdagi turniXdeb ol". Destructuring'ning tur darajasidagi egizagi.inferfaqatextendsdan keyin ishlaydi; tutilgan nom faqat?shoxida mavjud.ReturnType,Parameters,Awaited—inferbilan yozilgan. Async funksiya elementi:Awaited<ReturnType<typeof f>>[number].infer E extends string— tutilgan turga cheklov. Qolipga tushmasa — jimnever.inferfunksiya imzosiga ishonadi:anyqaytaradigan funksiyadananytutiladi va tekshiruv yo'qoladi.
Keyingi dars: Template literal types — satr turlarini shablon bilan yasash: `on${Capitalize<E>}` va yo'l parametrlari. U yerda infer satr ichida ham ishlashini ko'ramiz.
Manbalar
- TypeScript Handbook: "Conditional Types" — Inferring Within Conditional Types — typescriptlang.org/docs/handbook/2/conditional-types.html
- TypeScript Release Notes 2.8 (
infer), 4.5 (Awaited), 4.7 (extendsConstraints oninferType Variables) — typescriptlang.org/docs/handbook/release-notes/ lib.es5.d.ts— TypeScript bilan keladigan fayl (ReturnType,Parameters,Awaited)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!