Mundarija (37)
- Bu darsda
- 1. Nega bu kerak?
- 2. Generic type
- 2.1 Tur ham parametr oladi
- 2.2 Tur argumentini unutish
- 3. Generic interface
- 3.1 Sahifalangan ro'yxat
- 3.2 Generic turlar bir-birini o'raydi
- 3.3 Generic interfeys va uni amalga oshiradigan klass
- 4. Standart tur parametri
- 4.1 E = string
- 4.2 Tartib qoidasi
- 5. Generic klass
- 5.1 Oshxona navbati
- 5.2 Tur argumenti konstruktordan chiqariladi
- 5.3 static a'zolar T ni ko'rmaydi
- 5.4 Cheklovli generic klass: Repository<T>
- 6. Promise<T> va async
- 6.1 async funksiya doim Promise qaytaradi
- 6.2 Promise<ApiResponse<T>>
- 6.3 Promise.all turlarni o'rinma-o'rin saqlaydi
- 7. TypeScript ushlamaydigan tuzoq: javob turi — faqat va'da
- 8. Ko'p uchraydigan xatolar
- 8.1 Generic turga argument bermaslik
- 8.2 Standart parametrdan keyin majburiy
- 8.3 Bo'sh generic klass tur argumentisiz
- 8.4 static maydonda T
- 8.5 async funksiya natijasini Promise siz yozish
- 8.6 Tur argumentiga any berish
- 8.7 Bir xil tuzilishli turlarni nusxalash
- 9. Mashqlar
- 1-mashq (oson): Turni yig'ing
- 2-mashq (o'rta): Generic Stack<T>
- 3-mashq (qiyin): Amaliy tajriba — navbat va repository
- 10. Real ishda
- Xulosa
- Manbalar
TypeScript generic interface, type va klass: ApiResponse<T> va Queue<T>
Qisqacha: Tur parametri faqat funksiyada emas,
type,interfacevaclassda ham bo'ladi:type ApiResponse<T> = { ok: true; data: T } | { ok: false; error: string }. EndiApiResponse<Dish[]>,ApiResponse<Order>— bitta qolipdan yasalgan aniq turlar. Generic klass (class Queue<T>) esa har nusxasi o'z turi bilan ishlaydigan konteyner.
Bu darsda
- Generic
typevainterfaceyozasiz:ApiResponse<T>,Page<T>. - Standart tur parametrini (
<T, E = string>) ishlatasiz va uning qoidasini bilasiz. - Generic klass yozasiz: navbat
Queue<T>vaRepository<T extends …>. Promise<T>niasyncfunksiyalar bilan to'g'ri tiplaysiz.- "Javob turi — faqat va'da" tuzog'ini taniysiz.
Oldin bilishingiz kerak: Generic cheklovlar va keyof, Discriminated union va to'liqlik tekshiruvi, Klasslar TypeScript'da, async / await.
1. Nega bu kerak?
O'tgan darsda generic funksiyalarga cheklov qo'ydik. Endi «Bahor» ilovasining server bilan ishlashiga qaraymiz. Har so'rov javobi bir xil tuzilishda keladi: muvaffaqiyat bo'lsa — ok: true va ma'lumot, bo'lmasa — ok: false va xato matni. Sardor har javob uchun alohida tur yozdi:
type MenuResponse =
| { ok: true; data: string[] }
| { ok: false; error: string };
type OrderResponse =
| { ok: true; data: { id: number; total: number } }
| { ok: false; error: string };
const menu: MenuResponse = { ok: true, data: ["Osh", "Manti"] };
console.log(menu.ok && menu.data.length); // 2Ikki tur — deyarli bir xil. O'nta so'rov bo'lsa — o'nta nusxa. Ertaga xato qismiga code qo'shish kerak bo'lsa, o'n joyni tahrirlash kerak va bittasi albatta unutiladi. Bizga tur qolipi kerak: "javob — muvaffaqiyat (T turidagi ma'lumot bilan) yoki xato".
2. Generic type
2.1 Tur ham parametr oladi
Generics darsida funksiya tur parametri olardi. type ham xuddi shunday:
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string };
type Dish = { name: string; price: number };
const menu: ApiResponse<Dish[]> = {
ok: true,
data: [{ name: "Osh", price: 35000 }],
};
const failed: ApiResponse<Dish[]> = {
ok: false,
error: "Tarmoq yo'q",
};
console.log(menu.ok, failed.ok); // true falseApiResponse<T> — generic tur (turlar uchun qolip). ApiResponse<Dish[]> — qolipga Dish[] qo'yilgan aniq tur: { ok: true; data: Dish[] } | { ok: false; error: string }. Xato qismi bir marta yozilgan — o'zgartirish ham bir joyda.
Bu Discriminated union — ok maydoni ajratuvchi. Toraytirish ham avvalgidek ishlaydi:
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string };
function show(res: ApiResponse<string[]>): string {
if (!res.ok) {
return `Xato: ${res.error}`;
}
return res.data.join(", ");
}
console.log(show({ ok: true, data: ["Osh", "Manti"] })); // Osh, Manti
console.log(show({ ok: false, error: "500" })); // Xato: 500!res.ok shoxida res.error bor, undan keyin esa res.data — string[]. T toraytirishga xalaqit bermaydi.
2.2 Tur argumentini unutish
Generic tur chaqirilgan funksiya emas — u o'zi tur argumentini chiqara olmaydi. Uni yozish shart:
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string };
const res: ApiResponse = { ok: false, error: "500" };Kompilyator:
TS2314: Generic type 'ApiResponse' requires 1 type argument(s).Tarjimasi: "ApiResponse generic turi 1 ta tur argumentini talab qiladi". TypeScript aytyapti: "qolip berdingiz, lekin uning bo'sh joyini to'ldirmadingiz — data nima turda?" Massiv va tuple turlari darsidagi Array siz xabarning o'zi. Array<T> ham — TypeScript'ning o'zi yozgan generic tur.
3. Generic interface
3.1 Sahifalangan ro'yxat
interface ham tur parametri oladi. Masalan, serverdan buyurtmalar sahifama-sahifa keladi:
interface Page<T> {
items: T[];
page: number;
total: number;
}
type Order = { id: number; table: number };
const first: Page<Order> = {
items: [{ id: 101, table: 7 }],
page: 1,
total: 23,
};
console.log(first.items[0]?.table, first.total); // 7 23Page<Order> — buyurtmalar sahifasi, Page<Dish> — taomlar sahifasi. Tuzilish bir xil, element turi har xil.
Noto'g'ri element — aniq xato:
interface Page<T> {
items: T[];
total: number;
}
const names: Page<string> = { items: [1, 2], total: 2 };Kompilyator:
TS2322: Type 'number' is not assignable to type 'string'.
TS2322: Type 'number' is not assignable to type 'string'.Tarjimasi: "number turini string turiga berib bo'lmaydi" — har son uchun bittadan. Page<string> da items — string[], biz esa sonlar berdik.
3.2 Generic turlar bir-birini o'raydi
Generic turlarni ichma-ich qo'yish mumkin: ApiResponse<Page<Order>> — "javob: muvaffaqiyat bo'lsa — buyurtmalar sahifasi". O'qishni ichkaridan boshlang: Order → uning sahifasi → shu sahifaning javobi. Kompilyator ham qoliplarni ichkaridan tashqariga to'ldiradi:
| Qadam | Nima bo'ldi? | Natija |
|---|---|---|
| 1 | Page<T> ga T = Order qo'yildi |
{ items: Order[]; page: number; total: number } |
| 2 | ApiResponse<T> ga 1-qadam natijasi qo'yildi |
{ ok: true; data: Page<Order> } | { ok: false; error: string } |
| 3 | res.ok tekshirildi — birinchi variant qoldi |
res.data.items — Order[] |
Siz bilgan tayyor turlar ham aynan shunday yozilgan. TypeScript'ning lib fayllarida interface Array<T> { … }, interface Map<K, V> { … }, interface Promise<T> { … } bor. Ular siz yozayotgan generic interfeyslardan hech narsasi bilan farq qilmaydi.
3.3 Generic interfeys va uni amalga oshiradigan klass
Interfeysda metodlar ham T dan foydalanadi. Masalan, ofitsiant buyurtma qoralamasini saqlaydigan "ombor" shartnomasi:
interface Store<T> {
get(key: string): T | undefined;
set(key: string, value: T): void;
}
class MemoryStore<T> implements Store<T> {
#data = new Map<string, T>();
get(key: string): T | undefined {
return this.#data.get(key);
}
set(key: string, value: T): void {
this.#data.set(key, value);
}
}
const drafts: Store<string> = new MemoryStore<string>();
drafts.set("stol-7", "2 osh, 1 choy");
console.log(drafts.get("stol-7")); // 2 osh, 1 choyclass MemoryStore<T> implements Store<T> — klassning T si interfeysning T siga uzatildi (Klasslar TypeScript'da darsidagi implements). Ertaga xuddi shu interfeysni localStorage bilan ishlaydigan BrowserStore<T> ham amalga oshirishi mumkin. Kod esa Store<string> bilan ishlayveradi — qaysi ombor ekanini bilmaydi ham.
type yoki interface — qaysi biri? type va interface darsidagi qoida generic'da ham o'zgarmaydi. Union kerak bo'lsa (ApiResponse) — faqat type. Klass amalga oshiradigan shartnoma yoki kengaytiriladigan obyekt shakli bo'lsa — ko'pincha interface.
Tekshirib ko'ring:
ApiResponse<Page<Order>>turidagiresdares.oktekshirilgandan keyinres.data.items[0]ning turi qanday (noUncheckedIndexedAccessyoqilgan)?
Javob
Order | undefined. res.data — Page<Order>, items — Order[], indeks bo'yicha element esa bayroq tufayli undefined bo'lishi ham mumkin.
4. Standart tur parametri
4.1 E = string
Ko'p javoblarda xato — oddiy satr. Lekin ba'zan xato kodli obyekt kerak. Ikkinchi tur parametrini qo'shamiz va unga standart qiymat beramiz:
type ApiResponse<T, E = string> =
| { ok: true; data: T }
| { ok: false; error: E };
type ApiError = { code: number; message: string };
const simple: ApiResponse<number> = { ok: false, error: "Topilmadi" };
const detailed: ApiResponse<number, ApiError> = {
ok: false,
error: { code: 404, message: "Topilmadi" },
};
console.log(simple.ok, detailed.ok); // false falseE = string — standart tur parametri (default type parameter): argument berilmasa, E — string. ApiResponse<number> avvalgidek ishlaydi, kerak bo'lganda esa ApiResponse<number, ApiError>. Bu funksiyalardagi standart parametr qiymatining (function f(x = 5)) tur darajasidagi aksi.
4.2 Tartib qoidasi
Standart qiymatli parametrlar oxirida turishi kerak:
type Pair<A = string, B> = [A, B];Kompilyator:
TS2706: Required type parameters may not follow optional type parameters.Tarjimasi: "Majburiy tur parametrlari ixtiyoriylaridan keyin kela olmaydi". Funksiya parametrlaridagi qoidaning o'zi: ixtiyoriylar — oxirida.
Standart qiymat cheklov bilan birga ham yoziladi: <T extends object = Record<string, unknown>> — "T obyekt bo'lsin, berilmasa — istalgan kalitli lug'at".
Tekshirib ko'ring:
type Result<T = void, E = Error> = …—Resultni argumentsiz yozsa bo'ladimi?Result<string>daEnima?
Javob
Bo'ladi: ikkala parametrning ham standart qiymati bor, Result — Result<void, Error>. Result<string> da T = string, E esa standart — Error. Argumentlar chapdan o'ngga to'ldiriladi, qolganlari standartdan olinadi.
5. Generic klass
5.1 Oshxona navbati
Queue va deque darsida navbatni yozgansiz: birinchi kelgan buyurtma birinchi tayyorlanadi. Endi uni istalgan element turi bilan ishlaydigan qilamiz:
class Queue<T> {
#items: T[] = [];
enqueue(item: T): void {
this.#items.push(item);
}
dequeue(): T | undefined {
return this.#items.shift();
}
get size(): number {
return this.#items.length;
}
}
const kitchen = new Queue<string>();
kitchen.enqueue("Osh");
kitchen.enqueue("Manti");
console.log(kitchen.dequeue(), kitchen.size); // Osh 1class Queue<T> — generic klass. T klassning hamma joyida ishlatiladi: maydonda (T[]), parametrda (item: T), natijada (T | undefined). new Queue<string>() — satrlar navbati: endi kitchen.enqueue(35000) — TS2345.
shift() — massiv boshidan olib tashlaydi va O(n) vaqt oladi. O'sha darsdagi bosh ko'rsatkichli navbat tezroq edi. Bu yerda tezlik emas, turlar muhim — algoritmni almashtirsangiz ham, klass tashqi ko'rinishi (Queue<T>) o'zgarmaydi.
5.2 Tur argumenti konstruktordan chiqariladi
Konstruktor T turidagi qiymat olsa, new paytida tur argumentini yozish shart emas:
class Box<T> {
readonly value: T;
constructor(value: T) {
this.value = value;
}
}
const price = new Box(35000);
const dish = new Box("Osh");
console.log(price.value + 5000, dish.value.length); // 40000 3Hover'da price: Box<number>, dish: Box<string>. Lekin Queue ning konstruktori hech narsa olmaydi — undan T ni chiqarib bo'lmaydi. new Queue() yozilsa, tur Queue<unknown> bo'ladi (Generics darsidagi "chiqarilmasa — unknown" qoidasi). Shuning uchun bo'sh konteynerda tur argumentini yozing: new Queue<string>().
5.3 static a'zolar T ni ko'rmaydi
class Queue<T> {
static empty: T[] = [];
}Kompilyator:
TS2302: Static members cannot reference class type parameters.Tarjimasi: "Statik a'zolar klassning tur parametrlariga murojaat qila olmaydi". Mantiqi: T har nusxa uchun alohida (Queue<string>, Queue<number>), static a'zo esa bitta — klassning o'ziga tegishli (static a'zolar). U qaysi T ni olishini bilmaydi.
5.4 Cheklovli generic klass: Repository<T>
Ma'lumotlarni id bo'yicha saqlaydigan klass — real loyihalarda ko'p uchraydigan repository naqshi. Unga cheklov kerak: element id ga ega bo'lsin:
class Repository<T extends { readonly id: number }> {
#items = new Map<number, T>();
add(item: T): void {
this.#items.set(item.id, item);
}
findById(id: number): T | undefined {
return this.#items.get(id);
}
}
type Guest = { readonly id: number; name: string; people: number };
const guests = new Repository<Guest>();
guests.add({ id: 1, name: "Dilshod aka", people: 4 });
console.log(guests.findById(1)?.name); // Dilshod akaBu Generic cheklovlar darsidagi T extends … ning klassdagi shakli. Klass ichida item.id ishlatildi, tashqarida esa findById aniq Guest qaytardi — ?.name ishladi. new Repository<{ name: string }>() esa xato beradi: TS2741: Property 'id' is missing in type '{ name: string; }' but required in type '{ readonly id: number; }'. — tur argumenti cheklovni bajarmadi.
Generic klassning tuzilishini chizmada ko'ramiz:
classDiagram
class Repository~T~ {
-items Map~number, T~
+add(item T) void
+findById(id number) T
}
class Guest {
+id number
+name string
}
class Order {
+id number
+total number
}
Repository~T~ ..> Guest : T = Guest
Repository~T~ ..> Order : T = OrderBitta klass — ikki xil nusxa: Repository<Guest> va Repository<Order>. Har biri faqat o'z turidagi yozuvlarni qabul qiladi.
Tekshirib ko'ring:
const orders = new Repository<{ id: number; total: number }>(); orders.add({ id: 7 });— nima bo'ladi?
Javob
TS2741: Property 'total' is missing…: T — { id: number; total: number }, add esa aynan T ni kutadi. { id: 7 } da total yo'q. Klass cheklovi faqat kamida id ni talab qiladi, nusxaning T si esa to'liq turni.
6. Promise<T> va async
6.1 async funksiya doim Promise qaytaradi
async / await darsidan bilasiz: async funksiya natijasi — va'da (Promise). TypeScript'da bu Promise<T>: "oxir-oqibat T beradi".
type Dish = { name: string; price: number };
async function loadMenu(): Promise<Dish[]> {
return [{ name: "Osh", price: 35000 }];
}
const menu = await loadMenu();
console.log(menu[0]?.name); // Oshreturn da oddiy massiv yozdik — async uni o'zi Promise'ga o'raydi. await esa o'rovni ochadi: menu — Dish[]. (Bu blok modul, shuning uchun yuqori darajadagi await ishlaydi.)
Natija turini noto'g'ri yozish — keng tarqalgan xato:
async function countOrders(): number {
return 12;
}Kompilyator:
TS1064: The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'?Tarjimasi: "async funksiya yoki metodning natija turi global Promise<T> turi bo'lishi kerak. Promise<number> deb yozmoqchimidingiz?" TypeScript tuzatishni ham taklif qildi.
6.2 Promise<ApiResponse<T>>
Generic turlar Promise ichida ham ishlaydi — server bilan ishlaydigan funksiyaning odatiy imzosi:
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string };
async function fetchTables(): Promise<ApiResponse<number[]>> {
return { ok: true, data: [1, 2, 7] };
}
const res = await fetchTables();
if (res.ok) {
const list = res.data.join(", ");
console.log(`Bo'sh stollar: ${list}`); // Bo'sh stollar: 1, 2, 7
}Ichkaridan o'qing: number[] → uning javobi → shu javobning va'dasi. await dan keyin res — ApiResponse<number[]>, res.ok dan keyin res.data — number[].
6.3 Promise.all turlarni o'rinma-o'rin saqlaydi
Ikki so'rovni parallel yuborish — Promise kombinatorlari darsidagi Promise.all. TypeScript har o'rinning turini alohida kuzatadi:
async function loadMenu(): Promise<string[]> {
return ["Osh", "Manti"];
}
async function countOrders(): Promise<number> {
return 12;
}
const [menu, count] = await Promise.all([loadMenu(), countOrders()]);
console.log(menu.length + count); // 14Hover'da menu: string[], count: number — (string[] | number)[] aralashmasi emas. Promise.all ning turi generic: kirgan va'dalar ro'yxatidan tuple yasaydi va har o'ringa await qilingan turni qo'yadi. Generic'lar standart kutubxonani shunchalik aniq qiladi.
Tekshirib ko'ring:
async function f() { return [1, 2]; }— natija turini yozmagan bo'lsangiz,fning natija turi qanday chiqariladi?
Javob
Promise<number[]>. return dagi qiymat number[], async esa uni Promise ga o'raydi. Eksport qilinadigan funksiyalarda baribir yozib qo'yish tavsiya etiladi (Tur chiqarish darsidagi qoida) — imzo hujjat vazifasini o'taydi.
7. TypeScript ushlamaydigan tuzoq: javob turi — faqat va'da
ApiResponse<Dish[]> — bizning kutishimiz. Server esa uni bilmaydi. JSON.parse va response.json() any qaytaradi, any esa istalgan turga jim beriladi:
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string };
const raw = JSON.parse('{"ok": true, "data": null}');
const res: ApiResponse<string[]> = raw;
if (res.ok) {
console.log(res.data.length);
}tsc — xatosiz. Node esa:
TypeError: Cannot read properties of null (reading 'length')Server data: null yubordi. Tur "string[]" dedi, haqiqatda — null. Generic tur ma'lumotni tasvirlaydi, lekin uni tekshirmaydi. Server javobini tur bilan "yopishtirish" — as ning yashirin shakli.
Haqiqiy himoya — javobni unknown deb olib, ishlash paytida tekshirish. Bu darslarda o'rganganlaringiz bilan qisqa shakli:
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string };
function isStringArray(value: unknown): value is string[] {
return (
Array.isArray(value) && value.every((x) => typeof x === "string")
);
}
function toMenuResponse(raw: unknown): ApiResponse<string[]> {
if (typeof raw !== "object" || raw === null || !("data" in raw)) {
return { ok: false, error: "Javob shakli noto'g'ri" };
}
if (!isStringArray(raw.data)) {
return { ok: false, error: "data — satrlar ro'yxati emas" };
}
return { ok: true, data: raw.data };
}
const res = toMenuResponse(JSON.parse('{"ok": true, "data": null}'));
console.log(res.ok ? res.data.length : res.error);Konsolda:
data — satrlar ro'yxati emasraw: unknown—anyemas: tekshiruvsiz hech narsa qilib bo'lmaydi (any,unknown,never)."data" in rawvaisStringArray— toraytirish va tur predikati. Faqat ulardan keyinraw.datastring[]deb tan olinadi.- Buzuq javob endi
TypeErroremas, oddiyok: false— UI xato matnini ko'rsatadi.
Har javob uchun bunday funksiyani qo'lda yozish uzoq. Uni avtomatlashtirish — API ma'lumotlarini tiplash va Zod darslarining mavzusi.
8. Ko'p uchraydigan xatolar
8.1 Generic turga argument bermaslik
const r: ApiResponse = … — TS2314. Tuzatish: ApiResponse<Dish[]> yoki standart parametr qo'shing (<T = unknown>).
8.2 Standart parametrdan keyin majburiy
<A = string, B> — TS2706. Tuzatish: standart qiymatlilarni oxiriga.
8.3 Bo'sh generic klass tur argumentisiz
new Queue() — Queue<unknown>. Tuzatish: new Queue<Order>().
8.4 static maydonda T
TS2302. Tuzatish: statik a'zo umumiy bo'lsin (static readonly MAX = 50) yoki generic statik metod yozing (static from<U>(items: U[]): Queue<U>).
8.5 async funksiya natijasini Promise siz yozish
async function f(): number — TS1064. Tuzatish: Promise<number>.
8.6 Tur argumentiga any berish
ApiResponse<any>, new Queue<any>() — generic qolip bor, lekin ichi bo'sh: res.data yana any, xatolar jim o'tadi. Bu ko'pincha "tez ishlasin" deb yoziladi va keyin unutiladi. Tuzatish: aniq tur yozing. Hali noma'lum bo'lsa — unknown: u hech bo'lmasa ishlatishdan oldin tekshirtiradi. ESLint'ning no-explicit-any qoidasi (vazifalar da yoqilgan) bunday joylarni ko'rsatadi.
8.7 Bir xil tuzilishli turlarni nusxalash
MenuResponse, OrderResponse, GuestResponse — farqi faqat data turida. Tuzatish: bitta ApiResponse<T>. Turlarda ham kodga o'xshab "takrorlama" (DRY) qoidasi ishlaydi: tuzilish o'zgarsa, bir joyda o'zgaradi.
9. Mashqlar
1-mashq (oson): Turni yig'ing
type ApiResponse<T, E = string> =
| { ok: true; data: T }
| { ok: false; error: E };
interface Page<T> {
items: T[];
total: number;
}
const res: ApiResponse<Page<string>> = {
ok: true,
data: { items: ["Osh"], total: 1 },
};
console.log(res.ok && res.data.items.length); // 1res.data.items ning turi (ok tekshirilgandan keyin): [:string[]]. ApiResponse<number> dagi error ning turi: [:string]. ApiResponse da nechta tur parametri bor: .
Yechim
string[]:Page<string>dagiitems: T[],T = string.string:Eberilmadi — standart qiymat ishladi.- 2 ta:
T(majburiy) vaE(standart qiymatli).
2-mashq (o'rta): Generic Stack<T>
Oxirgi kelgan — birinchi chiqadigan stek klassini yozing (Stek darsidagi tuzilma): push(item), pop() (bo'sh bo'lsa undefined), peek(), size getter. Ichki massiv tashqaridan ko'rinmasin.
Yechim
class Stack<T> {
#items: T[] = [];
push(item: T): void {
this.#items.push(item);
}
pop(): T | undefined {
return this.#items.pop();
}
peek(): T | undefined {
return this.#items.at(-1);
}
get size(): number {
return this.#items.length;
}
}
const plates = new Stack<number>();
plates.push(1);
plates.push(2);
console.log(plates.pop(), plates.peek(), plates.size); // 2 1 1#items— haqiqatan yopiq (Klasslar TypeScript'da): tashqi kod stek tartibini buzolmaydi.popvapush— massivning oxirida, ikkalasi ham O(1).new Stack<number>()— konstruktor qiymat olmaydi, shuning uchun tur argumenti yozildi.
3-mashq (qiyin): Amaliy tajriba — navbat va repository
Mashq kurs/mashqlar/15/ papkasida. Ikki fayl:
22-generic-klass/queue.ts—Queue<T>:enqueue,dequeue,peek,size.22-generic-klass/repository.ts—ApiResponse<T, E = string>turi vaRepository<T extends { readonly id: number }>:add(item)(bandid—RangeError),findById(id)—ApiResponse<T>qaytarsin (topilmasaok: false),all().
Avval Repository ni generic'siz yozib ko'ring: ichkarida type Entity = { readonly id: number }, add(item: Entity), findById(id): Entity | undefined. Test faylda mehmon qo'shing ({ id: 1, name: "Malika" }) va uning ismini o'qing. npx tsc --pretty false nima deydi?
Yechim
Generic'siz variantda tsc 7.0.2 test faylda ikki xato topdi:
22-generic-klass/repository.test.ts(8,23): error TS2353: Object literal may only specify known properties, and 'name' does not exist in type 'Entity'.
22-generic-klass/repository.test.ts(9,36): error TS2339: Property 'name' does not exist on type 'Entity'.Birinchisi — add ga name berib bo'lmaydi: Entity da faqat id (ortiqcha xususiyat tekshiruvi). Ikkinchisi — topilgan yozuvda name yo'q. Klass faqat id ni biladi, qolgan hamma narsani "unutadi". Generic klass esa har nusxada to'liq turni saqlaydi.
// 22-generic-klass/queue.ts — oshxona navbati (FIFO)
export class Queue<T> {
#items: T[] = [];
enqueue(item: T): void {
this.#items.push(item);
}
dequeue(): T | undefined {
return this.#items.shift();
}
peek(): T | undefined {
return this.#items[0];
}
get size(): number {
return this.#items.length;
}
}// 22-generic-klass/repository.ts — id bo'yicha saqlanadigan yozuvlar
export type ApiResponse<T, E = string> =
| { readonly ok: true; readonly data: T }
| { readonly ok: false; readonly error: E };
export class Repository<T extends { readonly id: number }> {
#items = new Map<number, T>();
add(item: T): void {
if (this.#items.has(item.id)) {
throw new RangeError(`id band: ${item.id}`);
}
this.#items.set(item.id, item);
}
findById(id: number): ApiResponse<T> {
const item = this.#items.get(id);
if (item === undefined) {
return { ok: false, error: `Topilmadi: ${id}` };
}
return { ok: true, data: item };
}
all(): T[] {
return [...this.#items.values()];
}
}// 22-generic-klass/repository.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { Queue } from "./queue.ts";
import { Repository } from "./repository.ts";
type Guest = { readonly id: number; name: string };
test("Queue: birinchi kelgan — birinchi chiqadi", () => {
const kitchen = new Queue<string>();
kitchen.enqueue("Osh");
kitchen.enqueue("Manti");
assert.equal(kitchen.dequeue(), "Osh");
assert.equal(kitchen.peek(), "Manti");
assert.equal(kitchen.size, 1);
});
test("Repository: qo'shish va topish", () => {
const guests = new Repository<Guest>();
guests.add({ id: 1, name: "Malika" });
const found = guests.findById(1);
assert.equal(found.ok && found.data.name, "Malika");
const missing = guests.findById(2);
assert.equal(missing.ok ? "" : missing.error, "Topilmadi: 2");
});
test("Repository: band id — RangeError", () => {
const guests = new Repository<Guest>();
guests.add({ id: 1, name: "Malika" });
const twin = { id: 1, name: "Bobur" };
assert.throws(() => guests.add(twin), RangeError);
assert.equal(guests.all().length, 1);
});findByIdendiundefinedemas,ApiResponse<T>qaytaradi: chaqiruvchiokni tekshirmasdandatani ololmaydi, xato sababi ham keladi.found.ok && found.data.name—&&ning o'ng tomonidafoundallaqachon toraygan —databor.[...this.#items.values()]—Mapqiymatlaridan yangi massiv: tashqi kod ichkiMapni buzolmaydi.
npx tsc --pretty false — chiqishsiz. node --test 22-generic-klass/repository.test.ts bizda (Node 24.21.0) shunday chiqdi:
✔ Queue: birinchi kelgan — birinchi chiqadi (2.4746ms)
✔ Repository: qo'shish va topish (0.6098ms)
✔ Repository: band id — RangeError (1.3574ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 276.8654XATOLAR.md ga qo'shing: TS2314, TS2706, TS2302, TS1064. Keyin kurs/mashqlar papkasidan:
git add 15/22-generic-klass 15/XATOLAR.md
git commit -m "15/22: Queue<T> va Repository<T> — generic klasslar"10. Real ishda
- API qatlami: deyarli har frontend loyihada
ApiResponse<T>,Paginated<T>yoki shunga o'xshash turlar bor.vazifalarningturlar.tsfaylida ham shunday tur bor —Natija<T, E>(muvaffaqiyat yoki xato); u Async kod va xatolarni tiplash qadamida qo'shiladi. - Kutubxonalar: React'dagi
useState<T>,useRef<T>, server kutubxonalaridaRepository<User>— hammasi generic interfeys yoki klass. - Ma'lumotlar tuzilmalari:
Queue<T>,Stack<T>,LRUCache<K, V>— 14-qismdagi tuzilmalarning TypeScript'dagi tabiiy ko'rinishi. - Intervyu: "Generic klass yozing (stek yoki navbat)", "Standart tur parametri nima?", "Nega
statica'zoTni ko'rmaydi?" — tez-tez beriladi.
Xulosa
type,interfacevaclassham tur parametri oladi:ApiResponse<T>,Page<T>,Queue<T>. Generic tur — turlar uchun qolip.- Generic turga argument berish shart (
TS2314), yoki standart qiymat:<T, E = string>. Standartlilar — oxirida (TS2706). - Generic klassda
Thar nusxa uchun alohida; konstruktordan chiqariladi, bo'sh konteynerda esa yoziladi.statica'zolarTni ko'rmaydi. asyncfunksiya —Promise<T>.Promise<ApiResponse<T>>— server funksiyasining odatiy imzosi.- Generic tur ma'lumotni tasvirlaydi, tekshirmaydi: server javobi baribir ishlash paytida tekshiriladi.
Keyingi dars: Generics ilg'or amaliyoti — const tur parametrlari, NoInfer va generic'ni qachon yozmaslik kerak.
Manbalar
- TypeScript Handbook: "Generics" (Generic Types, Generic Classes, Generic Parameter Defaults) — typescriptlang.org/docs/handbook/2/generics.html
- TypeScript Handbook: "Classes" (Generic Classes) — typescriptlang.org/docs/handbook/2/classes.html#generic-classes
- TypeScript Handbook: "Everyday Types" (Promise) va "More on Functions" — typescriptlang.org/docs/handbook/2/everyday-types.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!