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
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
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!
? 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
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
- Funksiya parametrlariga tur beramiz; qaytish turi qavsdan keyin
: turshaklida yoziladi; ?โ ixtiyoriy parametr (majburiylardan keyin turishi shart);= qiymatโ standart parametr;- Generiklar turni parametr sifatida qabul qiladi:
function identity<T>(x: T): T; - Generiklar bir xil kodni turli turlar bilan xavfsiz qayta ishlatishga imkon beradi โ
anyga muqobil; - Interfeys va klasslar ham generik bo'lishi mumkin:
Quti<T>,Stek<T>; extendsorqali generik turga cheklov qo'yamiz:T extends Uzunlikli;keyofvaT[K]obyekt kalitlari va qiymatlari bilan aniq ishlashga yordam beradi.