TypeScript

Funksiyalar va generiklar

Funksiyalar โ€” har qanday dasturning asosi. TypeScript funksiyalarga ikkita muhim narsa qo'shadi: parametr turlari va qaytish turi. Ushbu darsda biz bularni, hamda TypeScript'ning eng kuchli va boshda biroz murakkab tuyuladigan imkoniyati โ€” generiklarni chuqur o'rganamiz.

Parametr va qaytish turlari

Funksiyada har bir parametrga tur beramiz, qaytish turini esa parametrlar qavsidan keyin, ikki nuqta orqali yozamiz:

function qoshish(a: number, b: number): number {
  return a + b;
}

console.log(qoshish(2, 3)); // 5

Bu yerda a: number va b: number โ€” parametr turlari; oxirdagi : number โ€” qaytish turi. Endi funksiyani noto'g'ri ishlatib bo'lmaydi:

qoshish(2, 3);        // OK
qoshish("2", 3);      // Xato: string number o'rniga
qoshish(2);           // Xato: ikkita argument kerak
qoshish(2, 3, 4);     // Xato: ortiqcha argument

Ok ifodali (arrow) funksiyalarda ham xuddi shunday:

const kopaytir = (a: number, b: number): number => a * b;

console.log(kopaytir(4, 5)); // 20
Qaytish turini ko'pincha yozmasa ham bo'ladi โ€” TypeScript uni tur xulosasi orqali o'zi topadi. Lekin uni ochiq yozish kodni o'qishni osonlashtiradi va tasodifiy xatolardan himoya qiladi.

Ixtiyoriy parametrlar (?)

Ba'zi parametrlar majburiy bo'lmasligi mumkin. Ularni ixtiyoriy qilish uchun nomidan keyin ? qo'yamiz. Ixtiyoriy parametr berilmasa, uning qiymati undefined bo'ladi:

function salom(ism: string, unvon?: string): string {
  if (unvon) {
    return "Salom, " + unvon + " " + ism;
  }
  return "Salom, " + ism;
}

console.log(salom("Ali"));            // Salom, Ali
console.log(salom("Ali", "janob"));  // Salom, janob Ali
Ixtiyoriy parametrlar har doim majburiy parametrlardan keyin kelishi shart. function f(a?: string, b: number) โ€” xato, chunki ixtiyoriy parametr majburiydan oldin turibdi.

Standart (default) parametrlar

JavaScript'dagi kabi, parametrga standart qiymat berish mumkin. Argument berilmasa, shu qiymat ishlatiladi. Bunda tur ko'pincha standart qiymatdan avtomatik aniqlanadi:

function kutlash(ism: string, salom: string = "Salom"): string {
  return salom + ", " + ism + "!";
}

console.log(kutlash("Ali"));                // Salom, Ali!
console.log(kutlash("Vali", "Xush kelibsiz")); // Xush kelibsiz, Vali!
Standart qiymatli parametr avtomatik ixtiyoriy hisoblanadi โ€” unga alohida ? qo'yish shart emas.

Generiklar nima uchun kerak?

Tasavvur qiling, siz massivning birinchi elementini qaytaradigan funksiya yozmoqchisiz. Turini qanday belgilaymiz? Agar number[] desak, faqat sonlar uchun ishlaydi. Agar any[] desak, tur xavfsizligini yo'qotamiz:

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

let n = birinchi([1, 2, 3]); // n turi โ€” any (yomon!)

Bu yerda muammo: natija any bo'lib qoldi, ya'ni TypeScript endi nning haqiqiy turini bilmaydi. Yechim โ€” generiklar.

Generik funksiyalar

Generik โ€” bu turni parametr sifatida qabul qiladigan funksiya. Turni burchak qavslar ichida, odatda T harfi bilan e'lon qilamiz. Bu "tur o'zgaruvchisi" chaqirilganda haqiqiy turga aylanadi:

function birinchi<T>(arr: T[]): T {
  return arr[0];
}

let son = birinchi([1, 2, 3]);        // son turi โ€” number
let matn = birinchi(["a", "b", "c"]); // matn turi โ€” string

Bu yerda sehr shundaki: T โ€” bu qat'iy tur emas, balki "o'rin". Funksiya chaqirilganda TypeScript argumentga qarab T nima ekanini o'zi aniqlaydi. Massiv sonlardan iborat bo'lsa, T = number; matnlardan bo'lsa, T = string.

Klassik generik misol โ€” identity funksiyasi (kelgan qiymatni o'zgarishsiz qaytaradi):

function identity<T>(x: T): T {
  return x;
}

let a = identity<string>("salom"); // T ni ochiq berdik: string
let b = identity(42);              // T avtomatik: number
Turni ochiq berish (identity<string>(...)) mumkin, lekin ko'pincha shart emas โ€” TypeScript uni argumentdan o'zi aniqlaydi. T nomi shartli; U, K, Element kabi istalgan nom ishlatilishi mumkin.

Bir nechta tur parametri

Generik funksiya bir nechta tur parametri qabul qilishi mumkin. Ularni vergul bilan ajratamiz. Masalan, ikkita turdan juftlik (pair) yasaydigan funksiya:

function juftlik<K, V>(kalit: K, qiymat: V): [K, V] {
  return [kalit, qiymat];
}

let p = juftlik("yosh", 25);
// p turi โ€” [string, number]

Generik interfeys va klasslar

Generiklar faqat funksiyalarda emas โ€” interfeys va klasslarda ham ishlatiladi. Bu bir xil tuzilmani turli turlar bilan qayta ishlatishga imkon beradi. Generik interfeys:

interface Quti<T> {
  ichidagi: T;
  ol(): T;
}

let sonQuti: Quti<number> = {
  ichidagi: 42,
  ol() { return this.ichidagi; }
};

let matnQuti: Quti<string> = {
  ichidagi: "salom",
  ol() { return this.ichidagi; }
};

Generik klass โ€” masalan, oddiy stek (stack) tuzilmasi. U istalgan turdagi elementlarni saqlay oladi:

class Stek<T> {
  private elementlar: T[] = [];

  push(element: T): void {
    this.elementlar.push(element);
  }

  pop(): T | undefined {
    return this.elementlar.pop();
  }
}

let sonStek = new Stek<number>();
sonStek.push(1);
sonStek.push(2);
console.log(sonStek.pop()); // 2

Generik cheklovlar (extends)

Ba'zan generik turga cheklov qo'yish kerak โ€” masalan, "T qanday tur bo'lishi mumkin, lekin unda albatta length xossasi bo'lsin". Buning uchun extends ishlatiladi:

interface Uzunlikli {
  length: number;
}

function uzunlik<T extends Uzunlikli>(x: T): number {
  return x.length;
}

uzunlik("salom");      // OK โ€” string'da length bor
uzunlik([1, 2, 3]);    // OK โ€” massivda length bor
uzunlik(42);           // Xato: number'da length yo'q

Cheklovsiz generikda biz T haqida hech narsa bilmaymiz, shuning uchun uning xossalarini ishlata olmaymiz. T extends Uzunlikli deyish orqali TypeScript'ga "Tda albatta length bor" deb kafolat beramiz va shu xossani xavfsiz ishlatamiz.

Yana bir foydali naqsh โ€” keyof bilan obyekt kalitini cheklash. Bu obyektdan xavfsiz qiymat olish uchun:

function xossaOl<T, K extends keyof T>(obj: T, kalit: K): T[K] {
  return obj[kalit];
}

let user = { ism: "Ali", yosh: 25 };

xossaOl(user, "ism");  // OK โ€” turi string
xossaOl(user, "yosh"); // OK โ€” turi number
xossaOl(user, "boo");  // Xato: 'boo' user kaliti emas
keyof T โ€” obyekt Tning barcha kalitlaridan iborat union tur. T[K] esa "Tning K kalitidagi qiymatning turi" degani. Bu ikkalasi birga juda kuchli va aniq turlarni yozishga imkon beradi.

Xulosa