9-bo‘lim
Generiklar
Turni parametrga aylantirish, cheklovlar, keyof bilan kalitni tekshirish va generiklarning ikkita chegarasi.
Ushbu bo‘lim mundarijasi
Ba'zi funksiyalar turdan qat'i nazar bir xil ishlaydi: massivning birinchi elementini olish, ikkita qiymatni almashtirish, ro'yxatni filtrlash.
Ularni yozishning ikkita yo'li bor - biri turlarni yo'qotadi, ikkinchisi saqlaydi.
Muammo: any hamma narsani yo'qotadi #
function birinchi(royxat: any[]): any {
return royxat[0];
}
const n = birinchi(["Olma", "Anor"]);
console.log(n.toUpperCase());
console.log(n.toFixed(2));
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/any.ts 2>&1 | grep -oE "(^OLMA|TypeError.*)" | head -2
tsc: xato yo_q
OLMA
TypeError: n.toFixed is not a function
royxat satrlardan iborat edi, lekin any buni unutdi va
toFixed ga ruxsat berdi.
Yechim: turni parametrga aylantirish #
function birinchi<T>(royxat: T[]): T | undefined {
return royxat[0];
}
const a = birinchi(["Olma", "Anor"]);
const b = birinchi([12000, 25000]);
console.log(a?.toUpperCase(), b?.toFixed(2));
tsx src/generik.ts
OLMA 12000.00
<T> - tur parametri. Funksiya parametri qiymat qabul
qilgani kabi, tur parametri tur qabul qiladi.
Hech qayerda <string> yozilmadi: TypeScript argumentga qarab
T ni o'zi topdi. Bu - tur xulosasi.
Endi xato tutiladi:
function birinchi<T>(royxat: T[]): T | undefined {
return royxat[0];
}
const a = birinchi(["Olma", "Anor"]);
console.log(a?.toFixed(2));
tsc --noEmit 2>&1 | fold -sw 74
src/generik2.ts(6,16): error TS2551: Property 'toFixed' does not exist on
type 'string'. Did you mean 'fixed'?
Tur argumentini qo'lda berish #
function birinchi<T>(royxat: T[]): T | undefined {
return royxat[0];
}
const a = birinchi<string>(["Olma"]);
const b = birinchi<string | number>([12000, "Olma"]);
console.log(a, b);
tsx src/aniq.ts
Olma 12000
Xulosa noto'g'ri bo'lsa yoki kengroq tur kerak bo'lsa,
<...> ichida o'zingiz yozasiz. Mos kelmasa - xato:
function birinchi<T>(royxat: T[]): T | undefined {
return royxat[0];
}
const c = birinchi<number>(["Olma"]);
tsc --noEmit 2>&1 | fold -sw 74
src/aniq2.ts(5,29): error TS2322: Type 'string' is not assignable to type
'number'.
Bir nechta tur parametri #
function almashtir<A, B>(juft: [A, B]): [B, A] {
return [juft[1], juft[0]];
}
const r = almashtir(["Olma", 12000]);
console.log(r[0].toFixed(0), r[1].toUpperCase());
tsx src/ikki.ts
12000 OLMA
Kirishda [string, number], chiqishda [number, string] -
tartib ham kuzatilib boradi.
Cheklovlar #
Tur parametri haqida hech narsa ma'lum emas, shuning uchun uning maydonlariga murojaat qilib bo'lmaydi:
function nomini<T>(x: T): string {
return x.nomi;
}
tsc --noEmit 2>&1 | fold -sw 74
src/cheklov3.ts(2,12): error TS2339: Property 'nomi' does not exist on
type 'T'.
extends bilan shart qo'yamiz:
function nomini<T extends { nomi: string }>(x: T): string {
return x.nomi.toUpperCase();
}
console.log(nomini({ nomi: "Olma", narx: 12000 }));
console.log(nomini({ nomi: "Anor" }));
tsx src/cheklov.ts
OLMA
ANOR
Shart bajarilmasa:
function nomini<T extends { nomi: string }>(x: T): string {
return x.nomi.toUpperCase();
}
const m = { narx: 12000 };
nomini(m);
nomini({ narx: 25000 });
tsc --noEmit 2>&1 | fold -sw 74
src/cheklov2.ts(6,8): error TS2345: Argument of type '{ narx: number; }'
is not assignable to parameter of type '{ nomi: string; }'.
Property 'nomi' is missing in type '{ narx: number; }' but required in
type '{ nomi: string; }'.
src/cheklov2.ts(8,10): error TS2353: Object literal may only specify
known properties, and 'narx' does not exist in type '{ nomi: string; }'.
Ikkita xato, ikkita boshqa kod. 4-bobdagi qoida bu yerda ham ishlaydi: o'zgaruvchi orqali berilganda "maydon yetishmaydi", literal berilganda "ortiqcha maydon".
extends ikki ma'noda| Yozuv | Ma'nosi |
|---|---|
interface B extends A | Meros - B da A ning hammasi bor |
<T extends A> | Shart - T A ga mos kelishi kerak |
Ikkinchisida T aynan A emas: u A ning hamma
maydoniga ega istalgan tur bo'lishi mumkin. Shuning uchun
nomini({ nomi, narx }) ishladi va T ning narx i
saqlanib qoldi.
keyof bilan kalitni tekshirish #
Generiklarning eng foydali qo'llanilishi:
function ol<T, K extends keyof T>(obyekt: T, kalit: K): T[K] {
return obyekt[kalit];
}
const m = { nomi: "Olma", narx: 12000, bor: true };
const a = ol(m, "nomi");
const b = ol(m, "narx");
const c = ol(m, "bor");
console.log(a.toUpperCase(), b.toFixed(2), c);
tsx src/keyof.ts
OLMA 12000.00 true
Uchta chaqiruv, uchta turli qaytish turi: string, number,
boolean. Buni any bilan ham, union bilan ham qilib
bo'lmasdi.
| Yozuv | Ma'nosi |
|---|---|
keyof T | T ning kalitlari unioni |
T[K] | K kalitidagi qiymatning turi |
Noto'g'ri kalit:
function ol<T, K extends keyof T>(obyekt: T, kalit: K): T[K] {
return obyekt[kalit];
}
const m = { nomi: "Olma", narx: 12000, bor: true };
ol(m, "rang");
tsc --noEmit 2>&1 | fold -sw 74
src/keyof2.ts(6,7): error TS2345: Argument of type '"rang"' is not
assignable to parameter of type '"nomi" | "narx" | "bor"'.
Xabar ruxsat etilgan kalitlarni sanab berdi. 4-bobdagi indeks imzosi muammosining to'g'ri yechimi - aynan shu.
Generik turlar #
Tur aliaslari ham parametr qabul qiladi:
type Natija<T> =
| { ok: true; qiymat: T }
| { ok: false; xato: string };
function bol(a: number, b: number): Natija<number> {
if (b === 0) return { ok: false, xato: "nolga bolinmaydi" };
return { ok: true, qiymat: a / b };
}
for (const b of [4, 0]) {
const r = bol(100, b);
console.log(r.ok ? r.qiymat : r.xato);
}
tsx src/natija.ts
25
nolga bolinmaydi
Bu naqsh - natija turi - istisno tashlamasdan xatoni
qaytarishning tur bilan himoyalangan usuli. ok ajratuvchi
maydon bo'lgani uchun r.qiymat ga faqat tekshiruvdan keyin
kirish mumkin.
Standart qiymat ham berish mumkin:
type Quti<T = string> = { ichi: T };
const a: Quti = { ichi: "Olma" };
const b: Quti<number> = { ichi: 12000 };
console.log(a.ichi.toUpperCase(), b.ichi.toFixed(0));
tsx src/standart.ts
OLMA 12000
Birinchi chegara: T toraytirilmaydi #
function katta<T>(x: T): T {
if (typeof x === "string") return x.toUpperCase();
return x;
}
tsc --noEmit 2>&1 | fold -sw 74
src/torayt.ts(2,30): error TS2322: Type 'string' is not assignable to
type 'T'.
'T' could be instantiated with an arbitrary type which could be
unrelated to 'string'.
Xabar sababni to'liq tushuntirdi: kimdir katta<"olma">("olma")
deb chaqirishi mumkin, u holda T - "olma" literali, lekin
toUpperCase() string qaytaradi.
Yechim - qaytish turini kengaytirish:
function katta<T extends string | number>(x: T): T | string {
if (typeof x === "string") return x.toUpperCase();
return x;
}
console.log(katta("olma"), katta(12000));
tsx src/torayt2.ts
OLMA 12000
Ikkinchi chegara: T ni chaqiruvchi tanlaydi #
function yarat<T>(): T {
return {} as T;
}
const a = yarat<string>();
console.log(typeof a, a.length);
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/yarat.ts
tsc: xato yo_q
object undefined
a ning turi string deb e'lon qilindi, aslida esa bo'sh
obyekt qaytdi. a.length - undefined.
Sabab: T ni chaqiruvchi tanlaydi, funksiya emas. Funksiya
esa o'ziga noma'lum turdagi qiymatni yasay olmaydi - shuning
uchun as T yozilgan, ya'ni yolg'on.
function yarat<T>(): T shaklidagi imzoni ko'rsangiz,
to'xtab o'ylang: funksiya T haqida hech narsa bilmaydi,
demak ichida albatta as bor.
Xuddi shunday, tur parametri faqat bir marta ishlatilsa ham foyda yo'q:
function chop<T>(x: T): void // T bir marta - foydasi yo'q
function chop(x: unknown): void // shu bilan bir xil
Generik foyda beradi, qachonki T kamida ikki joyda
uchrasa - kirishda va chiqishda.
Promise ham generik #
async function narxi(nomi: string): Promise<number> {
return nomi === "Olma" ? 12000 : 0;
}
const n = await narxi("Olma");
console.log(n.toFixed(2));
tsx src/promis.ts
12000.00
Array<T>, Promise<T>, Map<K, V>, Set<T> - standart
kutubxonaning ko'p qismi generik. Asinxron kodni 17-bobda
batafsil ko'ramiz.
Xulosa #
- Generik - turni parametrga aylantirish;
<T>tur parametri deb ataladi. anykirish turini chiqishga uzatmaydi, generik esa uzatadi.- Odatda tur argumentini yozish shart emas - TypeScript uni argumentdan xulosa qiladi.
- Kerak bo'lsa
birinchi<string>(...)deb qo'lda berish mumkin. - Cheklovsiz
Thaqida hech narsa ma'lum emas;<T extends {...}>shart qo'yadi. <T extends A>daTaynanAemas - uAdan kengroq bo'lishi mumkin va qo'shimcha maydonlari saqlanadi.keyof Tkalitlar unionini,T[K]esa kalitdagi qiymat turini beradi - bu juftlik indeks imzosining xavfsiz o'rnini bosadi.- Tur aliaslari ham generik bo'ladi;
Natija<T>naqshi xatoni istisno tashlamasdan qaytaradi. Tfunksiya ichidatypeofbilan toraytirilmaydi - qaytish turini kengaytiring.- Tur parametri kamida ikki joyda uchramasa, generik qilishning foydasi yo'q.
Array<T>,Promise<T>,Map<K, V>- standart kutubxonaning generiklari.
any[]qabul qiladigan funksiya yozing va ish vaqtida yiqiting.- Uni generikka aylantirib, xato kompilyatsiyada tutilishini ko'ring.
- Tur argumentini qo'lda
<string>deb bering. - Noto'g'ri tur argumenti berib,
TS2322ni oling. - Ikkita tur parametrli
almashtirfunksiyasini yozing. - Cheklovsiz
Tning maydoniga murojaat qilib,TS2339ni oling. extends { nomi: string }qo'shib tuzating.ol<T, K extends keyof T>funksiyasini yozing va noto'g'ri kalit berib, ruxsat etilgan kalitlar ro'yxatini ko'ring.Natija<T>turini yozib, muvaffaqiyat va xato holatlarini ishlating.function katta<T>(x: T): TichidatoUpperCasechaqirib,TS2322xabarining ikkinchi qatorini o'qing.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.