9-bo‘lim

Generiklar

Turni parametrga aylantirish, cheklovlar, keyof bilan kalitni tekshirish va generiklarning ikkita chegarasi.

🕑 26 daqiqa o‘qish 📄 741 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Muammo: any hamma narsani yo'qotadi
  2. Yechim: turni parametrga aylantirish
  3. Tur argumentini qo'lda berish
  4. Bir nechta tur parametri
  5. Cheklovlar
  6. keyof bilan kalitni tekshirish
  7. Generik turlar
  8. Birinchi chegara: T toraytirilmaydi
  9. Ikkinchi chegara: T ni chaqiruvchi tanlaydi
  10. Promise ham generik
  11. Xulosa

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 #

TS
function birinchi(royxat: any[]): any {
  return royxat[0];
}

const n = birinchi(["Olma", "Anor"]);
console.log(n.toUpperCase());
console.log(n.toFixed(2));
Terminal
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/any.ts 2>&1 | grep -oE "(^OLMA|TypeError.*)" | head -2
Natija
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 #

TS
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));
Terminal
tsx src/generik.ts
Natija
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:

TS
function birinchi<T>(royxat: T[]): T | undefined {
  return royxat[0];
}

const a = birinchi(["Olma", "Anor"]);
console.log(a?.toFixed(2));
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
src/generik2.ts(6,16): error TS2551: Property 'toFixed' does not exist on
type 'string'. Did you mean 'fixed'?
Bir xil funksiya, ikki xil natija any bilan birinchi(royxat: any[]): any string[] kiradi any chiqadi .toFixed(2) ga ruxsat tsc jim turadi Ish vaqtida: TypeError generik bilan birinchi<T>(royxat: T[]): T | undefined string[] kiradi, T = string string | undefined chiqadi .toFixed(2) taqiqlanadi TS2551 darhol chiqadi Ish vaqtida: xato yo'q
Generik kirish turini chiqishga uzatadi, any esa uzmaydi

Tur argumentini qo'lda berish #

TS
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);
Terminal
tsx src/aniq.ts
Natija
Olma 12000

Xulosa noto'g'ri bo'lsa yoki kengroq tur kerak bo'lsa, <...> ichida o'zingiz yozasiz. Mos kelmasa - xato:

TS
function birinchi<T>(royxat: T[]): T | undefined {
  return royxat[0];
}

const c = birinchi<number>(["Olma"]);
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
src/aniq2.ts(5,29): error TS2322: Type 'string' is not assignable to type
'number'.

Bir nechta tur parametri #

TS
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());
Terminal
tsx src/ikki.ts
Natija
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:

TS
function nomini<T>(x: T): string {
  return x.nomi;
}
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
src/cheklov3.ts(2,12): error TS2339: Property 'nomi' does not exist on
type 'T'.

extends bilan shart qo'yamiz:

TS
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" }));
Terminal
tsx src/cheklov.ts
Natija
OLMA
ANOR

Shart bajarilmasa:

TS
function nomini<T extends { nomi: string }>(x: T): string {
  return x.nomi.toUpperCase();
}

const m = { narx: 12000 };
nomini(m);

nomini({ narx: 25000 });
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
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
YozuvMa'nosi
interface B extends AMeros - 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:

TS
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);
Terminal
tsx src/keyof.ts
Natija
OLMA 12000.00 true

Uchta chaqiruv, uchta turli qaytish turi: string, number, boolean. Buni any bilan ham, union bilan ham qilib bo'lmasdi.

YozuvMa'nosi
keyof TT ning kalitlari unioni
T[K]K kalitidagi qiymatning turi

Noto'g'ri kalit:

TS
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");
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
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:

TS
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);
}
Terminal
tsx src/natija.ts
Natija
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:

TS
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));
Terminal
tsx src/standart.ts
Natija
OLMA 12000

Birinchi chegara: T toraytirilmaydi #

TS
function katta<T>(x: T): T {
  if (typeof x === "string") return x.toUpperCase();
  return x;
}
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
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:

TS
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));
Terminal
tsx src/torayt2.ts
Natija
OLMA 12000

Ikkinchi chegara: T ni chaqiruvchi tanlaydi #

TS
function yarat<T>(): T {
  return {} as T;
}

const a = yarat<string>();
console.log(typeof a, a.length);
Terminal
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/yarat.ts
Natija
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.

Hech qanday parametrsiz generik - ogohlantirish belgisi

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:

Natija
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 #

TS
async function narxi(nomi: string): Promise<number> {
  return nomi === "Olma" ? 12000 : 0;
}

const n = await narxi("Olma");
console.log(n.toFixed(2));
Terminal
tsx src/promis.ts
Natija
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.
  • any kirish 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 T haqida hech narsa ma'lum emas; <T extends {...}> shart qo'yadi.
  • <T extends A> da T aynan A emas - u A dan kengroq bo'lishi mumkin va qo'shimcha maydonlari saqlanadi.
  • keyof T kalitlar 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.
  • T funksiya ichida typeof bilan 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.
Amaliy topshiriq
  1. any[] qabul qiladigan funksiya yozing va ish vaqtida yiqiting.
  2. Uni generikka aylantirib, xato kompilyatsiyada tutilishini ko'ring.
  3. Tur argumentini qo'lda <string> deb bering.
  4. Noto'g'ri tur argumenti berib, TS2322 ni oling.
  5. Ikkita tur parametrli almashtir funksiyasini yozing.
  6. Cheklovsiz T ning maydoniga murojaat qilib, TS2339 ni oling.
  7. extends { nomi: string } qo'shib tuzating.
  8. ol<T, K extends keyof T> funksiyasini yozing va noto'g'ri kalit berib, ruxsat etilgan kalitlar ro'yxatini ko'ring.
  9. Natija<T> turini yozib, muvaffaqiyat va xato holatlarini ishlating.
  10. function katta<T>(x: T): T ichida toUpperCase chaqirib, TS2322 xabarining ikkinchi qatorini o'qing.

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.