Mundarija (36)
- Bu darsda
- 1. Nega bu kerak?
- 2. G'oya: 20 qatorlik "sxema kutubxonasi"
- 3. Zod 4: birinchi sxema
- 3.1 O'rnatish
- 3.2 z.object va z.infer
- 3.3 Asosiy qurilish bloklari
- 4. parse va safeParse
- 4.1 parse — xato tashlaydi
- 4.2 safeParse — natija obyekti
- 4.3 O'zbekcha xabarlar
- 5. Ma'lumotni o'zgartirish: transform va coerce
- 5.1 Sana satrdan Date ga
- 5.2 Forma va API — bitta sxema
- 5.3 .brand() — tekshirilgan qiymatga tamg'a
- 6. Standard Schema va Valibot
- 6.1 Standard Schema — umumiy "rozetka"
- 6.2 Valibot — kichik muqobil
- 7. Kutubxona narxi: vazifalar uchun o'lchov
- 7.1 Bundlersiz brauzerda
- 7.2 Bundler bilan
- 7.3 Qaror
- 8. Ko'p uchraydigan xatolar
- 8.1 TS18048: 'result.data' is possibly 'undefined'.
- 8.2 Tur va sxemani alohida yozish
- 8.3 z.coerce bilan bo'sh qiymat
- 8.4 parse ni try siz
- 8.5 Narxni o'lchamasdan qo'shish
- 9. Mashqlar
- 1-mashq (oson): Turni o'qing
- 2-mashq (o'rta): Bron sxemasi
- 3-mashq (qiyin): Amaliy tajriba — menyu klienti Zod bilan
- 4-mashq: Vazifalar qadami — o'lchov
- 10. Real ishda
- Xulosa
- Manbalar
Zod va TypeScript: turni sxemadan chiqarish — z.object, z.infer, safeParse
Qisqacha: Zod — sxema kutubxonasi: ma'lumot shaklini bir marta
z.object({ … })bilan yozasiz va undan ikki narsa olasiz — ish vaqtidagi tekshiruv (Schema.parse(data)) va TypeScript turi (z.infer<typeof Schema>). Tur va tekshiruv endi bitta joyda, ular hech qachon ajralib qolmaydi.parsexato tashlaydi,safeParseesa{ success, data | error }qaytaradi. Kutubxonaning narxi bor: bundlersiz brauzerda Zod 4.6.5 — 95 modul va 233,7 KB, shuning uchunvazifalarda qo'lda yozilgan tekshiruv qoldi.
Bu darsda
- Zod 4.6.5 ni o'rnatib,
z.object,z.string(),z.number(),z.enumbilan sxema yozasiz. z.inferbilan turni sxemadan chiqarasiz va bu "sehr" qanday ishlashini 20 qatorlik o'z kutubxonangizda ko'rasiz.parsevasafeParseni, xato xabarlarini (o'zbekcha ham) ishlata olasiz.transform,z.coercevaz.input/z.outputfarqini bilasiz — forma va API uchun.- Standard Schema va Valibot'ni tanishasiz va kutubxona narxini o'lchangan raqamlar bilan baholaysiz.
Oldin bilishingiz kerak: API ma'lumotlarini tiplash, infer kalit so'zi, Utility types, Uchinchi tomon kutubxonalarini tiplash.
1. Nega bu kerak?
O'tgan darsda menyuni ikki marta yozdik:
// tur
type MenuItem = { id: number; nom: string; narx: number };
// tekshiruv
function parseMenu(data: unknown): MenuItem[] { /* 20 qator */ }Bir oydan keyin Jasur aka so'radi: "Menyuga turkum qo'shaylik". Sardor type MenuItem ga turkum ni qo'shdi. tsc darhol parseMenu ning return qatorini qizil qildi — yaxshi. Lekin keyinroq u nom ning minimal uzunligini o'zgartirdi — faqat parseMenu da. Tur buni bilmaydi va bilishi ham shart emas. Ikki joy — ikki haqiqat: ertami-kechmi ular ajralib qoladi.
Sxema kutubxonasi g'oyasi: shaklni bir marta yozish. Tekshiruv sxemadan ishlaydi, tur sxemadan chiqariladi.
Hayotiy o'xshatish: oshxonada retsept kartasi va ombor ro'yxati alohida yuritilsa, ertaga kartada "zira" bor, omborda yo'q. Bitta jadvaldan ham retsept, ham xarid ro'yxati chiqarilsa — ular doim mos.
2. G'oya: 20 qatorlik "sxema kutubxonasi"
Zod'ni o'rnatishdan oldin uning yuragini o'zimiz yozamiz. Sxema — bu shunchaki unknown oladigan va aniq tur qaytaradigan funksiya (o'tgan darsdagi parse… ning o'zi):
type Schema<T> = (value: unknown) => T;
type Infer<S> = S extends Schema<infer T> ? T : never;
const str: Schema<string> = (value) => {
if (typeof value !== "string") throw new TypeError("satr emas");
return value;
};
const num: Schema<number> = (value) => {
if (typeof value !== "number") throw new TypeError("son emas");
return value;
};
function object<S extends Record<string, Schema<unknown>>>(shape: S) {
return (value: unknown): { [K in keyof S]: Infer<S[K]> } => {
if (typeof value !== "object" || value === null) {
throw new TypeError("obyekt emas");
}
const source = value as Record<string, unknown>;
const result: Record<string, unknown> = {};
for (const key of Object.keys(shape)) {
result[key] = shape[key]!(source[key]);
}
return result as { [K in keyof S]: Infer<S[K]> };
};
}
const MenuItem = object({ nom: str, narx: num });
type MenuItem = Infer<typeof MenuItem>;
const osh: MenuItem = MenuItem({ nom: "Osh", narx: 35000, x: 1 });
console.log(osh); // { nom: 'Osh', narx: 35000 }Bu kodda 15-qismning uch darsi uchrashdi:
Infer<S>—inferkalit so'zi: "agarS—Schema<T>bo'lsa,Tni ol".{ [K in keyof S]: Infer<S[K]> }— Mapped types: sxemaning har kaliti uchun o'sha kalitdagi sxemaning natija turi.typeof MenuItem— qiymatdan tur: sxema — qiymat (funksiya), tur esa undan olinadi.
Nima bo'ldi? tsc type MenuItem = Infer<typeof MenuItem> ni qadamma-qadam hisoblaydi:
| Qadam | Nimani hisoblaydi | Natija |
|---|---|---|
| 1 | object({ nom: str, narx: num }) dagi S |
{ nom: Schema<string>; narx: Schema<number> } |
| 2 | K = "nom": Infer<S["nom"]> = Infer<Schema<string>> |
string |
| 3 | K = "narx": Infer<Schema<number>> |
number |
| 4 | typeof MenuItem — qaytgan funksiyaning turi |
(value: unknown) => { nom: string; narx: number } |
| 5 | Infer<…> funksiya natijasini "tutib oladi" |
{ nom: string; narx: number } |
Natija: MenuItem turi — { nom: string; narx: number }, lekin biz uni yozmadik. Sxemaga maydon qo'shsangiz, tur o'zi o'zgaradi. Endi noto'g'ri obyekt yozib ko'ramiz:
type Schema<T> = (value: unknown) => T;
type Infer<S> = S extends Schema<infer T> ? T : never;
declare const str: Schema<string>;
declare const num: Schema<number>;
declare function object<S extends Record<string, Schema<unknown>>>(
shape: S,
): Schema<{ [K in keyof S]: Infer<S[K]> }>;
const MenuItem = object({ nom: str, narx: num });
type MenuItem = Infer<typeof MenuItem>;
const lagmon: MenuItem = { nom: "Lag'mon", narx: "28 000" };Kompilyator:
TS2322: Type 'string' is not assignable to type 'number'.TypeScript aytyapti: "string ni number ga berib bo'lmaydi" — narx: "28 000" qatorida.
tsc sxemadan chiqarilgan turdan foydalandi: narx — number. Blokdagi declare — "bunday qiymat bor deb hisobla" degan e'lon (Declaration fayllari darsidan). Bu yerda bizga faqat turlar kerak edi.
Zod — xuddi shu g'oya, faqat 100 dan ortiq tekshiruvchi, chiroyli xato xabarlari va puxta turlar bilan.
Tekshirib ko'ring: Bizning
objectga{ nom: str, narx: num }berildi.oshdax: 1qayerga ketdi?
Javob
Natijaga o'tmadi: object faqat shape dagi kalitlarni aylanib chiqadi va yangi obyekt yasaydi. Ortiqcha maydonlar tashlanadi — o'tgan darsdagi oq ro'yxat qoidasi. Zod ham standart holda shunday qiladi.
3. Zod 4: birinchi sxema
3.1 O'rnatish
kurs/mashqlar/15/ papkasida:
npm install --save-exact zod@4.6.5Zod — oddiy dependency (devDependency emas): u dastur ishlayotganda ham kerak, chunki tekshiruv ish vaqtida bo'ladi. Turlari paketning ichida — @types/zod kerak emas (Uchinchi tomon kutubxonalarini tiplash). Bu darsning Zod misollari saytda «Ishga tushir» tugmasiz (zod sayt sahifasida yo'q) — ularni kurs/mashqlar/15/43-zod/ da node fayl.ts bilan bajaring. Biz har birini tsc 7.0.2 va Node 24.21.0 da tekshirdik.
3.2 z.object va z.infer
import { z } from "zod";
const MenuItem = z.object({
id: z.number().int().positive(),
nom: z.string().min(1),
narx: z.number().nonnegative(),
turkum: z.enum(["taom", "ichimlik"]),
});
type MenuItem = z.infer<typeof MenuItem>;
const raw: unknown = JSON.parse(
'{"id":1,"nom":"Osh","narx":35000,"turkum":"taom","x":1}',
);
const osh: MenuItem = MenuItem.parse(raw);
console.log(osh);Konsolda:
{ id: 1, nom: 'Osh', narx: 35000, turkum: 'taom' }z.number().int().positive()— zanjir: son, butun, musbat. Har qadam tekshiruvga yana bir shart qo'shadi.z.enum(["taom", "ichimlik"])— faqat shu ikki satrdan biri; turi —"taom" | "ichimlik"(literal union).MenuItemnomi ikki marta: biri qiymat (sxema), biri tur. TypeScript'da bunga ruxsat bor — qiymatlar va turlar alohida "nomlar maydonida" yashaydi. Zod hujjati ham shunday yozishni tavsiya qiladi.- Ortiqcha
xtushib qoldi —z.objectstandart holda noma'lum kalitlarni olib tashlaydi.
z.infer qanday tur chiqarganini tsc ning o'zi aytadi. Noto'g'ri maydonga murojaat qildik:
MenuItem.parse(raw).price;43-zod/menu.ts(13,21): error TS2339: Property 'price' does not exist on type '{ id: number; nom: string; narx: number; turkum: "ichimlik" | "taom"; }'.Xabardagi tur — sxemadan chiqarilgan: { id: number; nom: string; narx: number; turkum: "ichimlik" | "taom"; }. Bitta qator ham qo'lda yozilmagan.
3.3 Asosiy qurilish bloklari
| Zod | Tur | Izoh |
|---|---|---|
z.string(), z.number(), z.boolean() |
string, number, boolean |
|
z.array(S) |
S[] |
|
z.enum(["a", "b"]) |
"a" | "b" |
|
S.optional() / S.nullable() |
T | undefined / T | null |
|
z.union([A, B]) |
A | B |
z.object ichida .optional() li maydon — ixtiyoriy kalit (izoh?: string). Discriminated union uchun ham alohida vosita bor: z.discriminatedUnion("ok", [A, B]).
4. parse va safeParse
4.1 parse — xato tashlaydi
import { z } from "zod";
const Price = z.number().nonnegative();
try {
Price.parse("35 000");
} catch (error) {
console.log(error instanceof z.ZodError);
if (error instanceof z.ZodError) {
console.log(error.issues[0]?.message);
}
}Konsolda:
true
Invalid input: expected number, received stringparse muvaffaqiyatli bo'lsa — tekshirilgan qiymat, bo'lmasa — ZodError. Uning issues massivida har muammo: code ("invalid_type"), path (qaysi maydon), message.
4.2 safeParse — natija obyekti
Ko'pincha xatoni tashlash emas, ko'rsatish kerak (formada). safeParse hech qachon tashlamaydi:
import { z } from "zod";
const MenuItem = z.object({
id: z.number().int().positive(),
nom: z.string().min(1),
narx: z.number().nonnegative(),
turkum: z.enum(["taom", "ichimlik"]),
});
const result = MenuItem.safeParse({
id: 1, nom: "", narx: "35 000", turkum: "salat",
});
if (result.success) {
console.log(result.data.nom);
} else {
console.log(result.error.issues.length);
console.log(z.prettifyError(result.error));
}Konsolda:
3
✖ Too small: expected string to have >=1 characters
→ at nom
✖ Invalid input: expected number, received string
→ at narx
✖ Invalid option: expected one of "taom"|"ichimlik"
→ at turkumresult — discriminated union: success: true da data, false da error. Bu o'tgan darsdagi ApiResponse<T> ning o'zi. success ni tekshirmasdan result.data.nom yozsangiz, tsc:
43-zod/menu.ts(12,13): error TS18048: 'result.data' is possibly 'undefined'.Nega 'data' does not exist emas, possibly 'undefined'? Zod natija turini shunday yozgan: muvaffaqiyatsiz holatda ham data kaliti bor, faqat undefined. Shu tuzilishni o'zimiz yozib, Zod'siz ko'ramiz:
type SafeResult<T> =
| { success: true; data: T; error?: undefined }
| { success: false; data?: undefined; error: Error };
declare const result: SafeResult<{ nom: string }>;
console.log(result.data.nom);Kompilyator:
TS18048: 'result.data' is possibly 'undefined'.TypeScript aytyapti: "result.data undefined bo'lishi mumkin".
Ikkala holatda ham data kaliti borligi uchun result.data ga murojaat mumkin, lekin turi { nom: string } | undefined. result.data?.nom — undefined bo'lishi mumkin bo'lgan qiymat. To'g'risi esa success ni tekshirish: shunda data aniq bor.
Zod hamma xatoni bir vaqtda yig'adi (birinchisida to'xtamaydi): uchta buzuq maydon — uchta muammo. Formada foydalanuvchiga hammasini birdan ko'rsatish mumkin. z.prettifyError — o'qish uchun qulay matn.
4.3 O'zbekcha xabarlar
Zod 4 da tayyor tarjimalar bor, o'zbekchasi ham — z.locales.uz(). Kerakli joyga o'z matningizni ham yozish mumkin:
import { z } from "zod";
z.config(z.locales.uz());
const Order = z.object({
dish: z.string().trim().min(1, "Taom nomi bo'sh"),
quantity: z.number().int().min(1),
});
const result = Order.safeParse({ dish: " ", quantity: 0 });
if (!result.success) {
console.log(z.prettifyError(result.error));
}Konsolda:
✖ Taom nomi bo'sh
→ at dish
✖ Juda kichik: kutilgan number >=1
→ at quantityz.config — butun dastur uchun, bir marta. "Taom nomi bo'sh" — shu tekshiruvning o'z matni. E'tibor bering: Zod'ning o'zbekcha tarjimasi ba'zi so'zlarda ‘ belgisini ishlatadi (Noto‘g‘ri). Bizning kursda faqat oddiy ' — matn sizga muhim bo'lsa, o'zingiz yozing.
Tekshirib ko'ring: API javobini tekshirishda
parseyokisafeParse— qaysi biri? Forma uchun-chi?
Javob
API javobi buzuq bo'lsa — bu kutilmagan holat, server bilan kelishuv buzilgan: parse qulay, xato yuqoriga ko'tariladi va bitta joyda ushlanadi. Forma esa tez-tez noto'g'ri to'ldiriladi — bu normal holat: safeParse va issues dan har maydon yonida xabar. Qoida Async kod va xatolarni tiplash darsida: kutilgan xato — qiymat, kutilmagani — throw.
5. Ma'lumotni o'zgartirish: transform va coerce
5.1 Sana satrdan Date ga
O'tgan darsda JSON'da sana — satr ekanini aytdik. Zod tekshiradi va aylantiradi:
import { z } from "zod";
const Order = z.object({
dish: z.string().trim().min(1),
quantity: z.coerce.number().int().min(1),
createdAt: z.iso.datetime().transform((s) => new Date(s)),
});
type OrderInput = z.input<typeof Order>;
type Order = z.output<typeof Order>;
const order: Order = Order.parse({
dish: " Osh ", quantity: "2", createdAt: "2026-10-06T10:00:00Z",
});
const hour = order.createdAt.getUTCHours();
console.log(order.dish, order.quantity, hour);Konsolda:
Osh 2 10z.string().trim()— avval bo'sh joylarni kesadi, keyin tekshiradi.z.coerce.number()— kirishniNumber(…)bilan songa aylantiradi: formadan kelgan"2"→2. Ehtiyot bo'ling:Number("")—0,Number(null)—0. Shuning uchun keyin.min(1).z.iso.datetime()— ISO formatdagi sana-vaqt satri;.transform(…)— tekshiruvdan keyinDatega aylantiradi.
Endi sxemaning ikki turi bor: kirish va chiqish. z.input<typeof Order> — createdAt: string, quantity: unknown (coerce hamma narsani oladi). z.output (u z.infer bilan bir xil) — createdAt: Date, quantity: number. API klient kirishni yuboradi, ilova chiqishni ishlatadi.
5.2 Forma va API — bitta sxema
TypeScript va DOM darsidagi parseOrder(data: FormData) ni eslang — 20 qator. Zod bilan:
import { z } from "zod";
const OrderForm = z.object({
dish: z.enum(["osh", "manti", "lagmon"]),
quantity: z.coerce.number().int().min(1),
note: z.string().trim().default(""),
});
const data = new FormData();
data.append("dish", "manti");
data.append("quantity", "3");
const order = OrderForm.parse(Object.fromEntries(data));
console.log(order); // { dish: 'manti', quantity: 3, note: '' }Object.fromEntries(data) — FormData ni oddiy obyektga aylantiradi. .default("") — maydon yo'q bo'lsa, bo'sh satr. Xuddi shu sxema serverda ham ishlatilishi mumkin (24-qismdan keyin Node serverda) — forma va server bitta qoidani tekshiradi.
API klientda esa o'tgan darsdagi getJson(response, parse) ga sxemaning parse metodini berasiz: getJson(response, MenuItem.parse). Uning turi — (data: unknown) => MenuItem, ya'ni aynan kutilgan parse funksiyasi.
5.3 .brand() — tekshirilgan qiymatga tamg'a
Branded types darsida "brend" turni qo'lda yozgan edik: oddiy son bilan adashib ketmaydigan son. Zod buni sxemaning o'zida beradi — .brand():
import { z } from "zod";
const OrderId = z.number().int().positive().brand<"OrderId">();
type OrderId = z.infer<typeof OrderId>;
function cancel(id: OrderId): string {
return `Buyurtma #${id} bekor qilindi`;
}
const id = OrderId.parse(15);
console.log(cancel(id)); // Buyurtma #15 bekor qilindicancel(15) deb yozsangiz, tsc (biz sinadik):
error TS2345: Argument of type 'number' is not assignable to parameter of type 'number & $brand<"OrderId">'.
Type 'number' is not assignable to type '$brand<"OrderId">'.TypeScript aytyapti: "oddiy number ni number & $brand<"OrderId"> (tamg'ali son) kutgan parametrga berib bo'lmaydi". OrderId turidagi qiymatni olishning yagona yo'li — OrderId.parse(…), ya'ni tekshiruvdan o'tkazish. Funksiya imzosi "menga tekshirilgan id ber" deydi, va tsc buni kafolatlaydi. Ishlash paytida brend yo'q: id — oddiy 15.
6. Standard Schema va Valibot
6.1 Standard Schema — umumiy "rozetka"
Sxema kutubxonalari ko'p: Zod, Valibot, ArkType. Forma kutubxonalari, server freymvorklari har biriga alohida ulanishni yozishni xohlamaydi. Shuning uchun ular kelishib, Standard Schema degan umumiy interfeys yaratdi: har sxemada ~standard xususiyati bor, ichida validate(value) funksiyasi. Turlari — @standard-schema/spec paketida (1.1.0, faqat turlar).
Bitta funksiya — istalgan kutubxonaning sxemasi bilan:
import type { StandardSchemaV1 } from "@standard-schema/spec";
import { z } from "zod";
import * as v from "valibot";
function check<T>(
schema: StandardSchemaV1<unknown, T>,
data: unknown,
): T {
const result = schema["~standard"].validate(data);
if (result instanceof Promise) throw new TypeError("async sxema");
if (result.issues) {
const messages = result.issues.map((i) => i.message);
throw new TypeError(messages.join("; "));
}
return result.value;
}
const ZodPrice = z.number().nonnegative();
const ValiPrice = v.pipe(v.number(), v.minValue(0));
console.log(check(ZodPrice, 35000), check(ValiPrice, 5000));
try {
check(ValiPrice, -1);
} catch (error) {
console.log((error as Error).message);
}Konsolda:
35000 5000
Invalid value: Expected >=0 but received -1check hech qaysi kutubxonani "bilmaydi" — faqat standartni. React Hook Form (forma kutubxonasi, 17-qismdan keyin), tRPC kabi vositalar sxemani aynan shunday qabul qiladi: siz kutubxonani tanlaysiz, ular bilan ishlash o'zgarmaydi.
6.2 Valibot — kichik muqobil
Valibot (1.5.0) — o'sha g'oya, boshqa tuzilish. Zod'da metodlar zanjiri (z.number().int()), Valibot'da esa har tekshiruv — alohida funksiya, v.pipe bilan ulanadi:
import * as v from "valibot";
const MenuItem = v.object({
id: v.pipe(v.number(), v.integer(), v.minValue(1)),
nom: v.pipe(v.string(), v.minLength(1)),
narx: v.pipe(v.number(), v.minValue(0)),
turkum: v.picklist(["taom", "ichimlik"]),
});
type MenuItem = v.InferOutput<typeof MenuItem>;
const result = v.safeParse(MenuItem, {
id: 4, nom: "Ko'k choy", narx: 5000, turkum: "ichimlik",
});
console.log(result.success && result.output.nom); // Ko'k choyFunksiyalar alohida bo'lgani uchun bundler ishlatilmaganlarini tashlab yuboradi ("tree shaking", 16-qismda). Natija — juda kichik kod. Bu farq keyingi bo'limda raqamlarda.
7. Kutubxona narxi: vazifalar uchun o'lchov
7.1 Bundlersiz brauzerda
vazifalar da serverRoyxatiniTekshir — qo'lda yozilgan 25 qator. Uni Zod bilan almashtirsak-chi? Biz o'lchadik (Chrome 154, vazifalar dagi CSP va Trusted Types, Pages kabi gzip, sekin tarmoq — 150 ms kechikish va 1,6 Mbit/s, 5 o'lchov medianasi):
| Variant | Fayllar | Uzatildi (gzip) | Sahifa tayyor |
|---|---|---|---|
Qo'lda (paket.ts) |
0 | 0,8 KB | 373 ms |
| Zod 4.6.5 | 95 | 233,7 KB | 3 729 ms |
| Valibot 1.5.0 | 1 | 15,7 KB | 604 ms |
vazifalar da bundler yo'q (Build darsi): brauzer har modulni alohida so'raydi. Zod'ning ESM fayllari bir-birini import qiladi — 95 ta fayl zanjiri. Sekin tarmoqda sahifa 0,4 soniya o'rniga 3,7 soniyada tayyor bo'ldi. Bu — ilovaning o'z JavaScript'idan (81 KB) uch baravar katta.
Yana bir topilma: Zod tezlik uchun ichida new Function(…) bilan kod yasaydi (JIT). vazifalar ning Trusted Types siyosati buni taqiqlaydi — brauzer konsolida This document requires 'TrustedScript' assignment va bitta CSP buzilishi chiqdi. Zod buni o'zi aniqlab, sekinroq yo'lga o'tadi, lekin konsol xatosi qoladi. Yechim — z.config({ jitless: true }): JIT o'chadi, buzilish 0.
7.2 Bundler bilan
Bundler bu rasmni o'zgartiradi. Bir xil menyu sxemasini esbuild 0.28.2 bilan bitta faylga yig'dik (--bundle --minify):
- Zod 4.6.592,1 KB
- zod/mini 4.6.56,1 KB
- Valibot 1.5.01,7 KB
Manba: O'lchandi: esbuild 0.28.2 --bundle --minify, gzip -9, bitta MenuItem sxemasi, 2026-10-06
- Zod (
import { z } from "zod") — metodlar zanjiri klasslarda yashaydi, bundler ishlatilmagan metodlarni ajrata olmaydi: 92,1 KB. zod/mini— Zod'ning o'zi, lekin Valibot uslubida (z.string().check(z.minLength(1))): 6,1 KB.- Valibot — 1,7 KB.
7.3 Qaror
Biz qaror qildik: vazifalar da qo'lda yozilgan tekshiruv qoladi (API ma'lumotlarini tiplash qadamidagi serverRoyxatiniTekshir). Sabablar: bundler yo'q, tekshiriladigan shakl bitta (ServerRoyxati) va u allaqachon testlangan. Bitta funksiya uchun 233,7 KB va qo'shimcha 3,3 soniya — juda qimmat. Savol vazifalar bundler'ga (Vite) o'tganda, 16-qismda qayta ochiladi: Zod yoki Valibot o'shanda arzon bo'ladi (Tree shaking va minifikatsiya).
Qoida: kutubxona qo'shishdan oldin uning narxini o'lchang — o'lcham, so'rovlar soni, xavfsizlik siyosatiga ta'siri. Kichik loyihada qo'lda yozilgan 25 qator ko'pincha yetarli. Ko'p sxemali loyihada (formalar, API, server) — sxema kutubxonasi o'zini oqlaydi.
Tekshirib ko'ring: Nega Zod bundlersiz 233,7 KB, bundler bilan 92,1 KB?
Javob
Bundlersiz brauzer zod/index.js dan boshlab import zanjiridagi hamma faylni yuklaydi — 95 ta, kerakmi-yo'qmi. Bundler esa faqat haqiqatan ishlatilgan kodni oladi, bitta faylga birlashtiradi va siqadi (minify): bo'sh joylar, izohlar, uzun nomlar yo'qoladi. Valibot va zod/mini yana kichikroq — ularning tuzilishi ishlatilmagan funksiyalarni tashlashga qulay.
8. Ko'p uchraydigan xatolar
8.1 TS18048: 'result.data' is possibly 'undefined'.
safeParse natijasida success tekshirilmagan. Tuzatish: if (result.success) { … result.data … }.
8.2 Tur va sxemani alohida yozish
type MenuItem = { … } va const MenuItemSchema = z.object({ … }) — yana ikki haqiqat. Tuzatish: type MenuItem = z.infer<typeof MenuItem>.
8.3 z.coerce bilan bo'sh qiymat
z.coerce.number() bo'sh forma maydonini ("") 0 qiladi va o'tkazib yuboradi. Tuzatish: keyin .min(1) yoki .positive(); yoki z.string().min(1) dan keyin transform(Number).
8.4 parse ni try siz
Server javobida ZodError ushlanmay qoladi. Tuzatish: yuqori qatlamda bitta catch yoki safeParse.
8.5 Narxni o'lchamasdan qo'shish
Bundlersiz loyihaga 95 modul. Tuzatish: o'lchang (Network paneli, Lighthouse); kichik loyihada qo'lda tekshiruv yoki Valibot.
9. Mashqlar
1-mashq (oson): Turni o'qing
const Guest = z.object({
name: z.string(),
people: z.number().int(),
phone: z.string().optional(),
});
type Guest = z.infer<typeof Guest>;Guest turida people ning turi — [:number]. phone majburiymi (ha/yo'q)? [:yo'q].
Yechim
{ name: string; people: number; phone?: string | undefined }. .int() turni o'zgartirmaydi — TypeScript'da "butun son" turi yo'q, faqat number; .int() faqat ish vaqtida tekshiradi. .optional() — ixtiyoriy kalit. Tur va tekshiruv har doim ham bir xil narsani aytmaydi: tekshiruv qattiqroq bo'lishi mumkin.
2-mashq (o'rta): Bron sxemasi
«Bahor» bron formasi: guest (bo'sh emas, bo'sh joylari kesiladi), people (formadan satr keladi, 1 dan 12 gacha butun son), time ("SS:DD", bron 10:00–22:00). Sxema yozing va "19:30", "09:00" bilan sinang. Ishora: z.string().regex(/^\d{2}:\d{2}$/) (RegExp) va .refine((t) => …, "xabar") — o'zingizning tekshiruvingiz.
Yechim
import { z } from "zod";
// "SS:DD" da satr tartibi = vaqt tartibi
const isOpen = (t: string) => t >= "10:00" && t <= "22:00";
const Booking = z.object({
guest: z.string().trim().min(1, "Mehmon ismi bo'sh"),
people: z.coerce.number().int().min(1).max(12),
time: z
.string()
.regex(/^\d{2}:\d{2}$/, "Vaqt SS:DD bo'lsin")
.refine(isOpen, "Bron 10:00–22:00"),
});
const ok = Booking.safeParse({
guest: "Malika", people: "4", time: "19:30",
});
const early = Booking.safeParse({
guest: "Bobur", people: "2", time: "09:00",
});
console.log(ok.success, ok.data?.people);
console.log(early.success, early.error?.issues[0]?.message);Konsolda:
true 4
false Bron 10:00–22:00"09:00" >= "10:00" — satrlarni solishtirish: "SS:DD" formatida ikki xonali bo'lgani uchun alifbo tartibi vaqt tartibi bilan bir xil (regex buni kafolatlaydi). ok.data?.people — 4 son, "4" satr emas (coerce).
3-mashq (qiyin): Amaliy tajriba — menyu klienti Zod bilan
O'tgan darsdagi 42-api/menu-api.ts ni kurs/mashqlar/15/43-zod/menu-schema.ts ga ko'chiring va parseItem/parseMenu ni Zod sxemasiga almashtiring. MenuItem turi — z.infer. createMenuApi o'zgarmaydi, faqat parseMenu o'rniga Menu.parse. Testlar ham o'sha uchta, faqat narx xatosi endi ZodError. Oxirida qo'lda yozilgan va Zod versiyasining qator sonini solishtiring.
Yechim
// 43-zod/menu-schema.ts — menyu klienti: Zod sxemasi
import { z } from "zod";
export const MenuItem = z.object({
id: z.number().int().positive(),
nom: z.string().min(1),
narx: z.number().nonnegative(),
turkum: z.enum(["taom", "ichimlik"]),
});
export type MenuItem = z.infer<typeof MenuItem>;
export const Menu = z.array(MenuItem);
export type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; status: number; message: string };
export function createMenuApi(fetchFn: typeof fetch, base: string) {
return {
async list(): Promise<ApiResponse<MenuItem[]>> {
const response = await fetchFn(`${base}/menyu`);
if (!response.ok) {
const message = `HTTP ${response.status}`;
return { ok: false, status: response.status, message };
}
return { ok: true, data: Menu.parse(await response.json()) };
},
};
}await response.json() — any, lekin Menu.parse uni unknown deb qabul qiladi va tekshiradi: any bu yerda "oqib ketmaydi". Testlar (43-zod/menu-schema.test.ts) — 42-dagidek, faqat yuqorida import { z } from "zod"; qo'shiladi va bitta test o'zgaradi:
// tekshirilmaydi: menu-schema.test.ts dan parcha
test("narx satr — ZodError", async () => {
const body = OSH.replace("35000", '"35 000"');
const api = createMenuApi(fakeFetch(`[${body}]`), BASE);
await assert.rejects(api.list(), z.ZodError);
});node --test 43-zod/menu-schema.test.ts — bizda (Node 24.21.0):
✔ to'g'ri javob — tekshirilgan menyu (55.9298ms)
✔ narx satr — ZodError (2.7904ms)
✔ 500 — ok: false (1.9597ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 505.4249Tekshiruv qismi 23 qatordan (parseItem va parseMenu, oradagi bo'sh qator bilan) 8 qatorga (MenuItem, uning turi va Menu) tushdi va turni alohida yozish kerak emas. Narxi — node_modules/zod (npm'dagi o'lchami 6,1 MB, bundlersiz brauzerda 95 modul). Server (Node) uchun bu muammo emas — fayllar diskda. Brauzer uchun esa — «Kutubxona narxi» bo'limidagi raqamlar.
XATOLAR.md ga: TS18048 (safeParse natijasi), ZodError xabarlari (Invalid input: expected number, received string), z.coerce tuzog'i.
git add 15/43-zod 15/package.json 15/package-lock.json 15/XATOLAR.md
git commit -m "15/43: menyu sxemasi — Zod, z.infer, safeParse"4-mashq: Vazifalar qadami — o'lchov
Bu darsning vazifalar qadami — kod emas, o'lchov. O'tgan darsdagi refactor/api-tekshiruv commit'ining tanasida "Zod o'lchandi" deb yozilgan edi. O'lchovni o'zingiz takrorlang:
vazifalardan nusxa oling,assets/js/vendor/zod/ganode_modules/zodning ESM fayllarini ko'chiring (src,.cjs,.d.tssiz).serverRoyxatiniTekshirning Zod versiyasini yozing vadist/ni build qiling.- DevTools → Network: "Slow 4G" bilan sahifani qayta yuklang. So'rovlar soni, uzatilgan hajm va
Loadvaqtini yozing. Konsolda Trusted Types xatosi bormi?
Yechim
Bizning o'lchovimizda (Chrome 154, 150 ms kechikish va 1,6 Mbit/s): 97 so'rov (sahifa, ilova va 95 Zod moduli), 233,7 KB, sahifa 3,7 soniyada tayyor. DevTools'dagi "Slow 4G" boshqacha sozlangan — sizning raqamlaringiz farq qiladi, lekin nisbat (o'n baravarga yaqin) shunday qoladi. Vendor nusxa — 136 fayl, 1 058 930 bayt. z.config({ jitless: true }) siz konsolda This document requires 'TrustedScript' assignment xatosi chiqadi.
Xulosa — «Qaror» bo'limida: kanonda qo'lda tekshiruv qoladi, Zod — 16-qismda bundler bilan qayta ko'rib chiqiladi. Bu qadam commit qilinmaydi: o'lchov nusxasini o'chiring (git status toza bo'lsin).
10. Real ishda
- Zod — TypeScript ekotizimidagi eng mashhur sxema kutubxonasi: Next.js server amallari, tRPC, React Hook Form, Astro kontent kolleksiyalari uni ishlatadi. Ish e'lonlarida "Zod" tez-tez yoziladi.
- Server tomonda (24-qismdan keyin) — so'rov tanasi, URL parametrlari,
.envo'zgaruvchilarini tekshirish:z.object({ PORT: z.coerce.number() }).parse(process.env). - Valibot, ArkType — kichik o'lcham muhim bo'lgan brauzer ilovalarida. Standard Schema tufayli ularni almashtirish oson.
- Intervyu: "TypeScript turlari ish vaqtida tekshiradimi? Unda Zod nima uchun?", "
parsevasafeParsefarqi?", "z.inferqanday ishlaydi?".
Xulosa
- Sxema kutubxonasi shaklni bir marta yozadi: tekshiruv —
Schema.parse, tur —z.infer<typeof Schema>. Ikki haqiqat yo'q. z.infersehr emas:infer, mapped types vatypeof— 20 qatorlik o'z kutubxonamizda ham ishladi.parse—ZodErrortashlaydi;safeParse—{ success, data | error }, hamma muammolarissuesda.z.locales.uz()— o'zbekcha xabarlar.transform,z.coerce,default— ma'lumotni aylantiradi;z.inputvaz.outputfarqlanadi.- Standard Schema (
~standard) — Zod, Valibot va boshqalar uchun umumiy interfeys. - Narxi: bundlersiz Zod — 95 modul, 233,7 KB; bundler bilan 92,1 KB,
zod/mini6,1 KB, Valibot 1,7 KB.vazifalarda qo'lda tekshiruv qoldi.
Keyingi dars: Async kod va xatolarni tiplash — Promise<T>, catch (e: unknown) va Result<T, E>: kutilgan xatoni qiymat qilib qaytaramiz.
Manbalar
- Zod hujjatlari (v4): "Basic usage", "Defining schemas", "Customizing errors", "Codecs/transforms" — zod.dev
- Standard Schema — standardschema.dev,
@standard-schema/spec1.1.0 - Valibot hujjatlari — valibot.dev
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!