IlmHamroh
JavaScript Full-stack/15-qism. TypeScript13/47-dars18 daqiqa
Mundarija (28)

TypeScript custom type guard va assertion funksiyalar: x is T va asserts

Qisqacha: Tur predikati — natija turi value is Dish bo'lgan funksiya: true qaytarsa, TypeScript value ni Dish deb biladi. Shunday qilib uzun tekshiruvni bir marta yozib, hamma joyda if (isDish(x)) bilan ishlatasiz. Assertion funksiya (asserts value is Dish) esa boolean qaytarmaydi: noto'g'ri bo'lsa xato tashlaydi, qaytsa — tur toraygan. TypeScript bu funksiyalar ichini tekshirmaydi: predikat yolg'on gapirsa, tur ham yolg'on bo'ladi.

Bu darsda

  • Tekshiruvni funksiyaga chiqarasiz va value is T bilan uni toraytiradigan qilasiz.
  • Predikatni filter bilan ishlatib, unknown[] dan aniq turdagi massiv olasiz.
  • TypeScript 5.5 dan beri predikatlar qachon o'zi chiqarilishini bilasiz.
  • asserts value is T va asserts condition assertion funksiyalarini yozasiz.
  • Predikat va assertion'ni qachon tanlashni va ularning xavfini tushunasiz.

Oldin bilishingiz kerak: Discriminated union va to'liqlik tekshiruvi, Narrowing: turni toraytirish, any, unknown, never, void va object.

1. Nega bu kerak?

Nega TypeScript darsida va'da bergan edik: unknown qiymatning maydonlarini tekshirishni qulay funksiyaga aylantiramiz. Mana o'sha tekshiruv — «Bahor» menyusi fayldan o'qilyapti:

ts
type Dish = { name: string; price: number };

const value: unknown = JSON.parse('{"name":"Osh","price":35000}');
if (
  typeof value === "object" &&
  value !== null &&
  "name" in value &&
  typeof value.name === "string" &&
  "price" in value &&
  typeof value.price === "number"
) {
  console.log(`${value.name}: ${value.price}`); // Osh: 35000
}

Ishlaydi — o'tgan darslardagi toraytirishlarning hammasi bir shartda. Lekin bu olti qatorni menyu, bron va chek fayllarini o'qiydigan har joyda takrorlash kerak. Tabiiy fikr — funksiyaga chiqarish:

ts
type Dish = { name: string; price: number };

function isDish(value: unknown): boolean {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    "price" in value
  );
}

const value: unknown = JSON.parse('{"name":"Osh","price":35000}');
if (isDish(value)) {
  console.log(value.name);
}

Kompilyator:

text
TS18046: 'value' is of type 'unknown'.

TypeScript aytyapti: «value ning turi — unknown», ya'ni "hali hech narsa bilmayman". Tekshiruv funksiya ichida bo'ldi, lekin if ichida value hali ham unknown. Natija turi boolean TypeScript'ga faqat "ha yoki yo'q" deydi — nimaga "ha" ekanini emas. Bu bilimni funksiyadan tashqariga chiqarish uchun maxsus natija turi kerak. (Bu yerda : boolean ni o'zimiz yozdik. Yozmasak, TypeScript ba'zan predikatni o'zi chiqaradi — «TypeScript 5.5: predikat o'zi chiqariladi» bo'limida.)

2. Tur predikati: value is T

2.1 Natija turi — "agar true bo'lsa, bu Dish"

ts
type Dish = { name: string; price: number };

function isDish(value: unknown): value is Dish {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    typeof value.name === "string" &&
    "price" in value &&
    typeof value.price === "number"
  );
}

const value: unknown = JSON.parse('{"name":"Osh","price":35000}');
if (isDish(value)) {
  console.log(`${value.name}: ${value.price}`); // Osh: 35000
}

value is Dish — tur predikati (type predicate). O'qilishi: "bu funksiya true qaytarsa, value — Dish". Funksiya ichi oddiy boolean qaytaradi, lekin chaqiruvchi uchun natija endi toraytiruvchi. JSDoc darsida xuddi shu narsani @returns {qiymat is Record<string, unknown>} shaklida ko'rgansiz — bu uning TypeScript yozuvi.

Turni toraytiradigan har qanday tekshiruv — typeof, in, instanceof va shunday funksiya — tur qo'riqchisi (type guard) deyiladi: unknown darsidagi eshikdagi qorovul kabi: hujjatni tekshiradi va faqat to'g'ri qiymatni ichkariga o'tkazadi. O'zimiz yozgani — custom type guard (o'z tur qo'riqchimiz).

Shartnoma shunday: "TypeScript, tekshirishni men bajardim, natijamga ishon". Nomi odatda is… yoki o'zbekcha "-mi" bilan tugaydi — isDish, xabarmi (predikat nomlari).

2.2 else da ham ishlaydi

Predikat ikki tomonga toraytiradi. Parametr union bo'lsa, false tarmog'ida Dish chiqarib tashlanadi:

ts
type Dish = { name: string; price: number };
type Drink = { name: string; hot: boolean };

function isDish(item: Dish | Drink): item is Dish {
  return "price" in item;
}

function line(item: Dish | Drink): string {
  if (isDish(item)) {
    return `${item.name}: ${item.price} so'm`;
  }
  return `${item.name} (${item.hot ? "issiq" : "sovuq"})`;
}

const tea = { name: "Ko'k choy", hot: true };
console.log(line(tea)); // Ko'k choy (issiq)

else (bu yerda if dan keyingi qism) da item — Drink. Bu in bilan toraytirish bilan bir xil, faqat endi tekshiruvning nomi bor va uni qayta ishlatish mumkin.

2.3 filter bilan: aralash massivdan aniq tur

Predikatning eng qulay joyi — massivni filtrlash:

ts
type Dish = { name: string; price: number };

function isDish(value: unknown): value is Dish {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    typeof value.name === "string" &&
    "price" in value &&
    typeof value.price === "number"
  );
}

const raw: unknown[] = JSON.parse(
  '[{"name":"Osh","price":35000},{"name":"?"},null]',
);
const dishes = raw.filter(isDish);
console.log(dishes.length, dishes[0]?.price); // 1 35000

Hover: const dishes: Dish[]. filter ning predikat qabul qiladigan varianti bor: callback value is Dish bo'lsa, natija — Dish[]. Oddiy boolean callback'da esa natija unknown[] bo'lib qolardi.

2.4 Predikat qoidalari

TypeScript predikatning ichini tekshirmaydi, lekin ikki qoidaga qaraydi. Birinchisi — predikat turi parametr turiga sig'ishi kerak:

ts
function isFree(price: string | number): price is boolean {
  return price === 0;
}

Kompilyator:

text
TS2677: A type predicate's type must be assignable to its parameter's type.
  Type 'boolean' is not assignable to type 'string | number'.

string | number qiymat hech qachon boolean bo'la olmaydi — predikat ma'nosiz. Bu xabarni type va interface qadamida vazifalar da ko'rgan edik (interface VazifaMalumoti bilan). Ikkinchi qoida — funksiya boolean qaytarishi shart: return 1 — TS2322.

Tekshirib ko'ring: function isText(x: unknown): x is string { return true; } — tsc xato beradimi? isText(5) dan keyin nima bo'ladi?

Javob

tsc xato bermaydi: natija boolean, predikat turi (string) unknown ga sig'adi. Lekin funksiya yolg'on gapiradi — 5 uchun ham true. if (isText(5)) ichida TypeScript 5 ni satr deb hisoblaydi, .toUpperCase() esa ishlash paytida TypeError beradi. Bu xavf haqida — «TypeScript nimani ushlamaydi» bo'limida.

3. TypeScript 5.5: predikat o'zi chiqariladi

3.1 Natija turi yozilmasa

TypeScript 5.5 (2024-yil iyun) dan beri sodda tekshiruv funksiyalarida predikatni yozish shart emas — kompilyator uni o'zi chiqaradi (inferred type predicates). tsc --declaration ko'rsatgan turlar:

Funksiya Chiqarilgan natija turi
function isText(x: unknown) { return typeof x === "string"; } x is string
(x: string | null) => x !== null x is string
function isText(x: unknown): boolean { … } boolean (o'zingiz yozdingiz)
(x: number | null) => x !== null && x > 0 boolean

Eng ko'p sezadigan joy — filter:

ts
const tips = [5000, null, 10000, null];
const real = tips.filter((tip) => tip !== null);
console.log(real.reduce((sum, tip) => sum + tip, 0)); // 15000

Hover: const real: number[]. TypeScript 5.5 gacha bu (number | null)[] edi va sum + tip xato berardi. Endi callback'dan tip is number chiqarildi.

3.2 Qachon chiqarilmaydi

Jadvaldagi oxirgi qator: x !== null && x > 0. Nega predikat chiqmadi? Predikat ikki tomonlama va'da: true — "bu number", false — "bu number emas". x > 0 qo'shilgach, false endi null ni ham, -5 ni ham bildiradi. "false — demak null" deyish yolg'on bo'lardi, shuning uchun TypeScript boolean qoldirdi. Bunday holatda filtrlangan massiv (number | null)[] bo'lib qoladi.

Uchta qoidani eslang: natija turi yozilmagan, parametr funksiya ichida qayta yozilmagan va tekshiruv ikki tomonlama to'g'ri. Murakkabroq holatda predikatni o'zingiz yozing — u kodni o'qiyotgan odamga ham niyatni aytadi.

4. Assertion funksiyalar: asserts

4.1 asserts value is T — "qaytdimmi, demak to'g'ri"

Predikat if bilan ishlatiladi: "agar shunday bo'lsa…". Ba'zan esa boshqa narsa kerak: "shunday bo'lishi shart, aks holda davom etmaymiz". Fayl import qilinyapti — buzuq bo'lsa, butun import to'xtashi kerak:

ts
type Dish = { name: string; price: number };

function assertDish(value: unknown): asserts value is Dish {
  if (
    typeof value !== "object" ||
    value === null ||
    !("name" in value) ||
    typeof value.name !== "string" ||
    !("price" in value) ||
    typeof value.price !== "number"
  ) {
    throw new TypeError("Taom yozuvi buzuq");
  }
}

const value: unknown = JSON.parse('{"name":"Manti","price":30000}');
assertDish(value);
console.log(value.price + 5000); // 35000

asserts value is Dish — assertion funksiya: "bu funksiya xatosiz qaytsa, value — Dish". Funksiya boolean qaytarmaydi — u void, noto'g'ri qiymatda esa throw qiladi. Chaqiruvdan keyingi hamma qatorlarda value — Dish. if yo'q, chekinish yo'q — kod tekis davom etadi. JSDoc darsidagi @returns {asserts value is string} ning TypeScript shakli.

4.2 asserts condition — shart haqida

Ikkinchi ko'rinishi turni emas, shartni tasdiqlaydi:

ts
function check(
  condition: unknown,
  message: string,
): asserts condition {
  if (!condition) {
    throw new Error(message);
  }
}

function seatsLeft(total: number | null, taken: number): number {
  check(total !== null, "Zal sig'imi noma'lum");
  return total - taken;
}

console.log(seatsLeft(40, 12)); // 28

check(total !== null, …) dan keyin TypeScript shartni rost deb biladi: total — number. Node'ning node:assert modulidagi assert funksiyasi ham aynan shunday turlangan (asserts value):

ts
import assert from "node:assert/strict";

function lastPrice(prices: readonly number[]): number {
  const last = prices.at(-1);
  assert(last !== undefined, "Narxlar ro'yxati bo'sh");
  return last;
}

console.log(lastPrice([35000, 28000])); // 28000

prices.at(-1) — oxirgi element yoki undefined (bo'sh massivda). assert dan keyin last — number, shuning uchun return last natija turiga mos. Testlarda ham shunday: assert.ok(item !== undefined) dan keyingi qatorlarda item toraygan bo'ladi. O'z check funksiyangiz bilan node:assert farqi — faqat xato turi va xabarida.

4.3 Qoida: aniq tur yozilgan nom bilan chaqirish

Assertion funksiyani strelka funksiya qilib yozsangiz, bir talab bor:

ts
type Dish = { name: string; price: number };

const assertDish = (value: unknown): asserts value is Dish => {
  if (typeof value !== "object") throw new TypeError("buzuq");
};

const v: unknown = JSON.parse("{}");
assertDish(v);

Kompilyator:

text
TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.

Tarjimasi: "Assertion'lar chaqiruvdagi har bir nom aniq tur annotatsiyasi bilan e'lon qilinishini talab qiladi". const assertDish = … — o'zgaruvchining turi qiymatdan chiqarilgan, aniq yozilmagan. TypeScript kod oqimini tahlil qilayotganda chiqarilgan turlarni hisoblamaydi. Tuzatish: function assertDish(…) e'loni (eng sodda) yoki o'zgaruvchiga tur yozish.

Tekshirib ko'ring: Nega assertDish dan xatosiz qaytgach value Dish bo'ladi, lekin funksiyaning natija turi void kabi hech narsa qaytarmaydi?

Javob

Assertion funksiya ma'lumotni natija orqali emas, qaytish fakti orqali beradi. Noto'g'ri qiymatda u throw qiladi va keyingi qatorlarga yetib bo'lmaydi. Demak, keyingi qatorga yetib kelgan bo'lsak — tekshiruv o'tgan. TypeScript kod oqimi tahlilida shuni hisobga oladi (never — "qaytmaydigan" funksiya bilan bir mantiq).

5. Predikatmi yoki assertion?

flowchart TD
  A["Qiymatni tekshirish kerak"] --> B{"Noto'g'ri bo'lsa<br/>davom etish mumkinmi?"}
  B -- "ha: tashlab yuborish,<br/>boshqa yo'l" --> P["Predikat: value is T<br/>if (isDish(x)) …, filter(isDish)"]
  B -- "yo'q: to'xtash kerak" --> S["Assertion: asserts value is T<br/>assertDish(x); keyin x — T"]
Predikat x is T Assertion asserts x is T
Natija boolean qaytadi yoki throw
Ishlatilishi if, filter, find alohida qator
Noto'g'ri qiymatda false — boshqa tarmoq xato tashlanadi
Qayerda ro'yxatdan yaroqlilarini ajratish kirish nuqtasi: import, sozlama, test

vazifalar da ikkalasi ham bor (hali JSDoc'da, paket.js da): oddiyObyektmi — predikat (qiymat is Record<string, unknown>), malumotlarniTekshir — assertion (asserts malumotlar is VazifaMalumoti[]). Import qilingan faylda bitta buzuq vazifa bo'lsa, butun import rad etiladi — shuning uchun assertion. Eslang: type va interface qadamidagi TS2677 aynan shu assertion'da chiqqan edi. Discriminated union qadamida esa birinchi .ts predikat — sinxron.ts dagi xabarmi(malumot: unknown): malumot is SinxronXabar — paydo bo'ldi. paket moduli API ma'lumotlarini tiplash qadamida .ts ga o'tadi va bu JSDoc'lar TypeScript yozuviga aylanadi.

6. TypeScript nimani ushlamaydi: yolg'onchi predikat

Predikat — sizning va'dangiz. TypeScript uni tekshirmaydi, faqat ishonadi:

ts
type Dish = { name: string; price: number };

// Xato: faqat name tekshirilgan, price — yo'q
function isDish(value: unknown): value is Dish {
  return (
    typeof value === "object" && value !== null && "name" in value
  );
}

const value: unknown = JSON.parse('{"name":"Osh"}');
if (isDish(value)) {
  console.log(value.price.toFixed(0));
}

tsc — xatosiz. Node esa:

text
TypeError: Cannot read properties of undefined (reading 'toFixed')

Predikat price ni tekshirmadi, lekin "bu Dish" dedi. TypeScript value.price ni son deb hisobladi. Bu as (tur majburlash, keyingi darsda) kabi xavfli: kompilyatorning ishonchini siz boshqarasiz.

Himoya:

  • Predikat turning hamma majburiy maydonlarini tekshirsin. Tur o'zgarsa (yangi maydon), predikatni ham yangilang — buni test bilan mustahkamlang.
  • Predikatlarni bir joyda saqlang (masalan, tur yonida) va ularga alohida test yozing.
  • Murakkab sxemalar uchun tekshiruvni turdan avtomatik yasaydigan kutubxonalar bor — Zod darsida.

7. Ko'p uchraydigan xatolar

7.1 boolean natijali tekshiruv funksiyasi

function isDish(x: unknown): boolean — chaqiruvchi uchun toraymaydi (TS18046). Tuzatish: x is Dish.

7.2 Predikat turi parametrga sig'maydi

(price: string | number): price is boolean — TS2677. Tuzatish: predikat turi parametr turining bir qismi bo'lsin.

7.3 Strelka funksiyali assertion

const assertX = (…): asserts … => … — chaqiruvda TS2775. Tuzatish: function assertX(…).

7.4 Yarim tekshiruvli predikat

Bitta maydonni tekshirib, butun turni va'da qilish — TypeError ishlash paytida. Tuzatish: hamma majburiy maydonlar va test.

8. Mashqlar

1-mashq (oson): Natija turini to'ldiring

Predikat natijasi: x [:is] string — "true bo'lsa, x satr". Noto'g'ri qiymatda xato tashlaydigan funksiya natijasidagi kalit so'z: [:asserts]. Strelka funksiya qilib yozilgan assertion chaqirilganda chiqadigan xato kodi: TS.

Yechim
  • x is string — tur predikati: boolean qaytaradi, true da toraytiradi.
  • asserts — assertion funksiya: qaytsa — to'g'ri, aks holda throw. Shart uchun shakli — asserts condition.
  • TS2775 — assertion'ni chaqirishda nom aniq tur bilan e'lon qilingan bo'lishi kerak; function e'loni bu talabni o'zi bajaradi.

2-mashq (o'rta): Booking predikati va filter

Bron yozuvi: type Booking = { guest: string; people: number; time: string }. isBooking(value: unknown): value is Booking yozing (hamma maydon, people — 1 dan katta yoki teng butun son). Keyin JSON'dan o'qilgan aralash massivdan faqat yaroqli bronlarni ajrating va ularning odamlar sonini yig'ing.

Ishora: Number.isInteger toraytirmaydi (Narrowing darsi) — undan oldin typeof … === "number" yozing.

Yechim
ts
type Booking = { guest: string; people: number; time: string };

function isBooking(value: unknown): value is Booking {
  return (
    typeof value === "object" &&
    value !== null &&
    "guest" in value &&
    typeof value.guest === "string" &&
    "people" in value &&
    typeof value.people === "number" &&
    Number.isInteger(value.people) &&
    value.people >= 1 &&
    "time" in value &&
    typeof value.time === "string"
  );
}

const raw: unknown[] = JSON.parse(
  '[{"guest":"Malika","people":2,"time":"19:00"},' +
    '{"guest":"Bobur","people":0,"time":"20:00"},' +
    '{"guest":"Dilshod aka","people":4,"time":"20:30"}]',
);
const bookings = raw.filter(isBooking);
const people = bookings.reduce((sum, b) => sum + b.people, 0);
console.log(bookings.length, people); // 2 6

raw ga unknown[] deb tur yozdik: JSON.parse ning any ini darhol to'xtatdik. filter(isBooking) — Booking[], keyingi reduce da b.people — son. Boburning broni (0 kishi) tashlab yuborildi.

3-mashq (qiyin): Amaliy tajriba — menyu faylini o'qish

Mashq kurs/mashqlar/15/ da. Birinchi variant — boolean natijali tekshiruv:

ts
// 13-guards/menu.ts — menyu faylini o'qish (birinchi variant)
export type MenuItem = {
  readonly name: string;
  readonly price: number;
};

export function isMenuItem(value: unknown): boolean {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    typeof value.name === "string" &&
    "price" in value &&
    typeof value.price === "number"
  );
}

export function parseMenu(json: string): MenuItem[] {
  const data: unknown = JSON.parse(json);
  if (!Array.isArray(data)) {
    throw new TypeError("Menyu massiv emas");
  }
  const items: unknown[] = data;
  return items.filter(isMenuItem);
}
  1. npx tsc --pretty false — xatoni o'qing. Nega const items: unknown[] = data; qatori kerak bo'ldi? (Ishora: Array.isArray ni unknown ga qo'llasangiz, natija turi qanday? Hover qiling.)
  2. isMenuItem ni predikatga aylantiring. Qo'shimcha talab: nomi bo'sh bo'lmasin, narx — musbat butun son.
  3. assertMenu(value: unknown): asserts value is MenuItem[] yozing: massiv emas — TypeError("Menyu massiv emas"), N-yozuv buzuq — TypeError("N-yozuv buzuq"). parseMenu uni ishlatsin — bitta buzuq yozuv butun faylni rad etsin.
  4. validItems(values) — yumshoq variant: buzuqlarni tashlab yuboradi.
  5. Testlar: 6 xil noto'g'ri yozuv, yaroqli fayl, ikki xil TypeError (xabari bilan), validItems.
Yechim

1-qadamda tsc 7.0.2:

text
13-guards/menu.ts(24,3): error TS2322: Type 'unknown[]' is not assignable to type 'MenuItem[]'.
  Type 'unknown' is not assignable to type 'MenuItem'.

boolean callback bilan filter turni o'zgartirmaydi: unknown[] → unknown[]. items: unknown[] qatori nega kerak? Array.isArray(data) dan keyin data ning turi — any[] (tsc --declaration buni any[] deb ko'rsatadi). Bu — standart kutubxonadagi eski qaror. any[] bilan filter natijasi ham any[] bo'lardi va MenuItem[] ga jim mos kelardi — xato yashirinardi. unknown[] ga olib, any ni darhol to'xtatdik.

ts
// 13-guards/menu.ts — menyu faylini o'qish
export type MenuItem = {
  readonly name: string;
  readonly price: number;
};

// Tur predikati: true qaytsa — value MenuItem
export function isMenuItem(value: unknown): value is MenuItem {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    typeof value.name === "string" &&
    value.name.trim() !== "" &&
    "price" in value &&
    typeof value.price === "number" &&
    Number.isInteger(value.price) &&
    value.price > 0
  );
}

// Assertion: qaytsa — hammasi yaroqli; bitta buzuq yozuv — xato
export function assertMenu(
  value: unknown,
): asserts value is MenuItem[] {
  if (!Array.isArray(value)) {
    throw new TypeError("Menyu massiv emas");
  }
  value.forEach((item: unknown, index) => {
    if (!isMenuItem(item)) {
      throw new TypeError(`${index + 1}-yozuv buzuq`);
    }
  });
}

export function parseMenu(json: string): MenuItem[] {
  const data: unknown = JSON.parse(json);
  assertMenu(data);
  return data;
}

// Yumshoq variant: buzuq yozuvlarni tashlab yuboradi
export function validItems(values: readonly unknown[]): MenuItem[] {
  return values.filter(isMenuItem);
}
  • assertMenu(data); dan keyin return data; — data endi MenuItem[], hech qanday as siz.
  • forEach((item: unknown, index) => …) — item ga unknown deb tur yozdik, aks holda u any bo'lardi (any[] ning elementi).
  • Ikki xil xulq ikki funksiyada: parseMenu — qat'iy (assertion), validItems — yumshoq (predikat + filter). Qaysi biri kerakligi vaziyatga bog'liq: menyu importi — qat'iy, kunlik statistika — yumshoq.
ts
// 13-guards/menu.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { isMenuItem, parseMenu, validItems } from "./menu.ts";

test("isMenuItem: to'g'ri va noto'g'ri yozuvlar", () => {
  assert.equal(isMenuItem({ name: "Osh", price: 35000 }), true);
  const bad = [
    null,
    "Osh",
    { name: "Osh" },
    { name: " ", price: 5000 },
    { name: "Manti", price: "30000" },
    { name: "Choy", price: -5000 },
  ];
  for (const value of bad) {
    assert.equal(isMenuItem(value), false);
  }
});

test("parseMenu: yaroqli fayl", () => {
  const menu = parseMenu('[{"name":"Osh","price":35000}]');
  assert.equal(menu[0]?.price, 35000);
});

test("parseMenu: massiv emas va buzuq yozuv — TypeError", () => {
  assert.throws(() => parseMenu('{"name":"Osh"}'), {
    name: "TypeError",
    message: "Menyu massiv emas",
  });
  const json = '[{"name":"Osh","price":35000},{"name":"Somsa"}]';
  assert.throws(() => parseMenu(json), {
    name: "TypeError",
    message: "2-yozuv buzuq",
  });
});

test("validItems: buzuqlari tashlanadi", () => {
  const items = validItems([{ name: "Osh", price: 35000 }, null, {}]);
  assert.deepEqual(items, [{ name: "Osh", price: 35000 }]);
});

assert.throws ning ikkinchi argumenti — obyekt: xato nomi va xabari tekshiriladi. menu[0]?.price — mashqlar papkasida noUncheckedIndexedAccess yoqilgan (Massiv va tuple). npx tsc --pretty false — chiqishsiz. node --test 13-guards/menu.test.ts — bizda (Node 24.21.0):

text
✔ isMenuItem: to'g'ri va noto'g'ri yozuvlar (1.7419ms)
✔ parseMenu: yaroqli fayl (0.5174ms)
✔ parseMenu: massiv emas va buzuq yozuv — TypeError (1.5751ms)
✔ validItems: buzuqlari tashlanadi (1.257ms)
ℹ tests 4
ℹ suites 0
ℹ pass 4
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 291.7513

XATOLAR.md ga: TS2677 (predikat turi), TS2775 (strelkali assertion), TS2322 'unknown[]' … 'MenuItem[]'. Keyin kurs/mashqlar papkasidan:

bash
git add 15/13-guards 15/XATOLAR.md
git commit -m "15/13: menu.ts — is va asserts bilan tekshiruv"

9. Real ishda

  • Kirish nuqtalari — server javobi, localStorage, postMessage, fayl importi — predikat va assertion'larning asosiy joyi. Ichkaridagi kod esa allaqachon tekshirilgan turlar bilan ishlaydi.
  • Testlarda assert(x !== undefined) (node:assert) va Vitest/Jest'dagi shunga o'xshash funksiyalar — asserts ning kundalik ko'rinishi.
  • Kutubxonalar: Zod'ning schema.parse() — aslida avtomatik yasalgan assertion: o'tsa — tur aniq, o'tmasa — xato (Zod).
  • Intervyu: "Type guard nima va qanday yoziladi?", "is va asserts farqi?", "TS 5.5 dagi inferred type predicates nima?" — o'rta darajadagi savollar.

Xulosa

  • value is T — tur predikati: true qaytsa, chaqiruvchi uchun value — T. if, else, filter, find bilan ishlaydi.
  • TypeScript 5.5 dan beri sodda tekshiruvlarda predikat o'zi chiqariladi (filter((x) => x !== null) → aniq tur), lekin faqat ikki tomonlama to'g'ri bo'lsa.
  • asserts value is T — qaytsa, keyingi qatorlarda value — T; noto'g'ri bo'lsa throw. asserts condition — shartni tasdiqlaydi. Strelka emas, function bilan yozing (TS2775).
  • Predikat — va'da: TypeScript ichini tekshirmaydi. Yarim tekshiruv — ishlash paytidagi TypeError. Hamma maydon va test.
  • Kirishda — assertion (qat'iy) yoki predikat + filter (yumshoq).

Keyingi dars: Type assertion, non-null va satisfies — as bilan TypeScript'ni "ishontirish", ! operatori va turini yo'qotmasdan tekshiradigan satisfies.

Manbalar

  • TypeScript Handbook: "Narrowing" → Using type predicates, Assertion functions — typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
  • TypeScript Release Notes 3.7 (assertion functions) — typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html
  • TypeScript Release Notes 5.5 ("Inferred Type Predicates", 2024-06) — typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html
  • Node.js hujjatlari: assert(value[, message]) — nodejs.org/api/assert.html
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
TypeScript custom type guard va assertion funksiyalar: x is T va asserts — IlmHamroh