TypeScript generiklar (generics) asoslari

Massivdan birinchi elementni qaytaradigan funksiya kerak. Uni matnlar uchun yozdingiz. Endi sonlar uchun ham kerak bo’ldi — yana bir xil funksiya yozasizmi?

Yoki any ishlatasizmi? Unda oldingi darsdagi muammo qaytadi: natija tipi yo’qoladi.

Uchinchi yo’l bor — generik.

Muammoni ko’rsatamiz

any bilan yozilgan variant:

function firstItemAny(items: any[]): any {
  return items[0];
}

const city = firstItemAny(["Toshkent"]);
console.log(city.toFixed(2));

Bu kod kompilyatsiyadan o’tadi. Matnda toFixed yo’q, lekin TypeScript jim turadi — chunki any tekshiruvni o’chirdi.

Funksiya matn massivini oldi, lekin natija tipi haqidagi ma’lumot yo’qoldi.

Generic — tipni parametr sifatida uzatish

function firstItem<T>(items: T[]): T | undefined {
  return items[0];
}

const firstCity = firstItem(["Toshkent", "Samarqand"]);
const firstScore = firstItem([90, 85]);

console.log(firstCity, firstScore);
Toshkent 90

Endi TypeScript tipni saqlab qoldi: firstCitystring, firstScorenumber. Buni tekshirib ko’ring:

const bad: number = firstItem(["Aziz"]);
example.ts(4,7): error TS2322: Type 'string | undefined' is not assignable to type 'number'.
  Type 'undefined' is not assignable to type 'number'.

T — shunchaki nom, Type so’zining qisqartmasi. Uni istalgan nom bilan almashtirish mumkin, lekin T keng tarqalgan kelishuv.

Tip qanday aniqlanadi

Ko’p holda tipni yozish shart emas — TypeScript uni argumentdan oladi:

// T avtomatik string bo'ldi
const name = firstItem(["Aziz"]);

Ba’zan uni aniq ko’rsatish kerak bo’ladi, masalan massiv bo’sh bo’lsa:

console.log(firstItem<number>([]));
undefined

Bo’sh massivdan TypeScript hech narsa aniqlay olmaydi, shuning uchun <number> qo’lda yoziladi.

extends bilan cheklash

Ba’zan generik tip haqida biror narsa bilish kerak bo’ladi. Masalan funksiya name maydoniga murojaat qiladi:

function longestName<T extends { name: string }>(items: T[]): string {
  let longest = "";

  for (const item of items) {
    if (item.name.length > longest.length) {
      longest = item.name;
    }
  }

  return longest;
}

console.log(longestName([{ name: "Aziz" }, { name: "Malika" }]));
Malika

T extends { name: string } — “T har qanday tip bo’lishi mumkin, lekin unda name maydoni bo’lishi shart” degani.

Shart bajarilmasa, xato chiqadi:

console.log(longestName([{ title: "Kitob" }]));
example.ts(4,28): error TS2353: Object literal may only specify known properties, and 'title' does not exist in type '{ name: string; }'.

Cheklashsiz item.name yozib bo’lmaydi: TypeScript T da bunday maydon borligini bilmaydi.

Generic tip taxallusi

Generik faqat funksiyada emas, tip e’lonida ham ishlatiladi. Eng ko’p uchraydigan misol — server javobi:

type ApiResponse<T> = {
  data: T;
  success: boolean;
};

type User = { id: number; name: string };

const userResponse: ApiResponse<User> = {
  data: { id: 1, name: "Aziz" },
  success: true,
};

const listResponse: ApiResponse<string[]> = {
  data: ["a", "b"],
  success: true,
};

console.log(userResponse.data.name);
console.log(listResponse.data.length);
Aziz
2

Bitta shakl, turli ichki tiplar. success maydoni har javobda bir xil, data esa har safar boshqacha.

Bu naqsh fetch bilan ishlaganda juda qo’l keladi: server javobining tashqi qobig’i doim bir xil bo’ladi.

Generic sinf

class Box<T> {
  private items: T[] = [];

  add(item: T): void {
    this.items.push(item);
  }

  getAll(): T[] {
    return this.items;
  }
}

const numberBox = new Box<number>();
numberBox.add(10);
numberBox.add(20);

console.log(numberBox.getAll());
[ 10, 20 ]

numberBox.add("matn") yozsangiz xato chiqadi: quti sonlar uchun yaratilgan. Klasslar darsidagi sinf shu tarzda tip bilan himoyalanadi.

Siz generiklarni allaqachon ishlatgansiz

Array<string> yozuvini massiv va kortej darsida ko’rgansiz — bu generik edi. Promise<string> ham shunday: “matn bilan tugaydigan va’da”.

async function loadName(): Promise<string> {
  return "Aziz";
}

loadName().then((name) => console.log(name.toUpperCase()));
AZIZ

Promise<string> tufayli .then ichidagi name avtomatik string bo’ladi. Promise darsidagi kod TypeScript’da shu tarzda tekshiriladi.

Qachon generik yozish kerak

Holat Yechim
Funksiya bir nechta tip bilan ishlaydi va tipni saqlashi kerak Generik
Tip noma’lum va ishlatilmaydi unknown
Tip aniq bitta Oddiy tip yozing

Amaliy mashq

  1. firstItem<T> funksiyasini yozib, uni matn va son massivlari bilan chaqiring;
  2. Natijani noto’g’ri tipdagi o’zgaruvchiga berib, TS2322 xatosini o’qing;
  3. Bo’sh massiv bilan chaqirib, tipni <number> shaklida qo’lda ko’rsating;
  4. lastItem<T> funksiyasini o’zingiz yozing — qaytish tipida undefined ni unutmang;
  5. T extends { id: number } cheklovli funksiya yozib, id siz obyekt berib ko’ring;
  6. ApiResponse<T> tipini e’lon qilib, uni ikki xil ichki tip bilan ishlating.

4-qadam eng muhimi: qaytish tipiga undefined qo’shishni unutish generik yozishdagi eng tipik xato.

Qisqacha xulosa

  • Generik tipni parametr sifatida qabul qiladi va uni chiqishda saqlaydi.
  • any dan farqi shu: any tipni yo’qotadi, generik uni olib o’tadi.
  • Tip odatda argumentdan avtomatik aniqlanadi; kerak bo’lsa <number> shaklida qo’lda beriladi.
  • extends generik tipga talab qo’yadi va funksiya ichida maydonlarga murojaat qilishga ruxsat beradi.
  • Generik tip taxallusi (ApiResponse<T>) bitta shaklni turli ichki tiplar bilan ishlatadi.
  • Tip parametri bir marta ishlatilsa, generik ortiqcha — unknown yetarli.

Keyingi darsda utility tiplarPartial, Pick, Omit va Record.

Bu dars foydali bo'ldimi?

marta ko'rildi