Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. any — yuqumli kasallik
- 2.1 Bitta any — butun zanjir
- 2.2 To'xtatish: unknown
- 3. Turni qayerda yozish kerak
- 3.1 Ortiqcha annotatsiya
- 3.2 Chegaralarda — ochiq yozing
- 4. as, ! va @ts-ignore — xatoni yashirish
- 4.1 as — va'da, tekshiruv emas
- 4.2 as qachon to'g'ri
- 4.3 ! va @ts-ignore
- 4.4 ESLint — avtomatik nazoratchi
- 5. Haddan tashqari murakkab turlar
- 5.1 "Aqlli" tur — kimga kerak?
- 5.2 type-fest — tayyor utility turlar
- 5.3 ts-reset — standart turlarni tuzatish
- 6. Yaxshi odatlar — bir sahifada
- 7. 15-qism xaritasi
- 8. 15-qism yakuni: vazifalar v5
- 9. Ko'p uchraydigan xatolar
- 8.1 : any bilan "tez yechim"
- 8.2 Har qatorga tur
- 8.3 as bilan xatoni yo'qotish
- 8.4 TS2589 — tur juda chuqur
- 8.5 ts-reset'ni kutubxonaga qo'shish
- 10. Mashqlar
- 1-mashq (oson): Anti-patternni toping
- 2-mashq (o'rta): satisfies bilan menyu
- 3-mashq (qiyin): Amaliy tajriba — "tez yozilgan" savatni tozalash
- 11. Real ishda
- Xulosa
- Manbalar
TypeScript best practices va anti-patternlar: any, as, ortiqcha turlar — 15-qism yakuni
Qisqacha: Yaxshi TypeScript kodi turlarni kam, lekin aniq yozadi: ichkarida tur chiqarishga ishonadi, chegaralarda (eksport funksiya, tashqi ma'lumot) esa turni ochiq yozadi. To'rt asosiy anti-pattern:
any(u yuqumli — tekshiruvni butun zanjir bo'ylab o'chiradi), ortiqcha annotatsiya,as/!/@ts-ignorebilan xatoni yashirish va o'qib bo'lmaydigan "aqlli" turlar. Yordamchilar: ESLint qoidalari (typescript-eslint), tayyor utility turlar to'plamitype-festva standart turlarni xavfsizroq qiladigants-reset.
Bu darsda
anyqanday tarqalishini va uniunknownbilan qanday to'xtatishni ko'rsatasiz.- Turni qayerda yozish, qayerda TypeScript'ga qoldirish kerakligini bilasiz.
as,!,@ts-ignoreo'rniga tekshiruv vasatisfiesishlatasiz.- Murakkab turdan qachon voz kechishni,
type-festvats-resetnima berishini bilasiz. - 15-qismni yakunlaysiz:
vazifalarv5 — 2 721 qator TypeScript, birortaanyvaassiz.
Oldin bilishingiz kerak: TS xato xabarlarini o'qish, any, unknown, never, void va object, Type assertion, non-null va satisfies, Rekursiv turlar va tur murakkabligi chegaralari, JavaScript'dan TypeScript'ga ko'chish.
1. Nega bu kerak?
15-qism davomida TypeScript'ning deyarli hamma vositasini ko'rdik: literal va discriminated union'lar, generics, mapped va conditional turlar, tsconfig bayroqlari, dekoratorlar, Zod. Vositani bilish — yarim ish. Ikkinchi yarmi — qachon ishlatmaslik.
Boshlovchi TypeScript kodida ikki xil kasallik uchraydi. Biri — turlar juda kam: hamma joyda any, as, ! — TypeScript bor, lekin u hech narsani tekshirmaydi. Ikkinchisi — turlar juda ko'p: har qatorda ortiqcha annotatsiya va hech kim o'qiy olmaydigan 30 qatorlik "sehrli" tur. Ikkalasi ham kodni yomonlashtiradi: birinchisi xavfsizlikni, ikkinchisi o'qilishni yo'qotadi.
Hayotiy o'xshatish: «Bahor» oshxonasidagi yorliqlar. Hech bir idishda yorliq bo'lmasa — tuzni shakar bilan adashtirasiz. Har qoshiqqa yorliq yopishtirilsa — oshxonada ishlab bo'lmaydi. Yorliq — muhim joyda: ziravor bankalarida, sovutgichdagi idishlarda.
2. any — yuqumli kasallik
2.1 Bitta any — butun zanjir
function parseConfig(text: string): any {
return JSON.parse(text);
}
const config = parseConfig('{"tip": "10"}');
const tipRate = config.tip;
const total = 80000 * (1 + tipRate);
console.log(total); // 8800000tsc — 0 xato. Natija — 8 800 000 so'm, 88 000 o'rniga. Nima bo'ldi? "tip" satr bo'lib keldi ("10", 0.1 emas): 1 + "10" — "110", 80000 * "110" — 8 800 000. Hover'larga qarang: config — any, tipRate — any, total — number. any dan olingan har narsa ham any, va any bilan arifmetika "son" deb hisoblanadi — total hatto ishonchli ko'rinadi.
Bu any ning asosiy xavfi: u faqat bitta joyni emas, undan "oqib" o'tgan hamma qiymatni tekshiruvdan chiqaradi. Bitta parseConfig(): any — butun ilovada teshik.
2.2 To'xtatish: unknown
function parseConfig(text: string): unknown {
return JSON.parse(text);
}
const config = parseConfig('{"tip": "10"}');
const total = 80000 * (1 + config.tip);Kompilyator:
TS18046: 'config' is of type 'unknown'.TypeScript aytyapti: "config ning turi — unknown" — undan tip ni tekshirmasdan olib bo'lmaydi.
unknown — "avval tekshir" (API ma'lumotlarini tiplash). tsc endi tekshiruv yozishga majbur qiladi va 8 800 000 so'mlik xato ishlab chiqarishga yetib bormaydi.
Qoidalar:
anyo'rniga deyarli doimunknown. Eslatma:noImplicitAny(strictichida) faqat yashirinanyni ushlaydi — o'zingiz yozgan: anyni emas. Uni typescript-eslint'ningno-explicit-anyqoidasi ushlaydi.anyning manbalari kod ichida emas, tashqarida:JSON.parse,response.json(),.catch((e) => …), eski@typespaketlari. Ularni chegarada darholunknownga aylantiring.- Haqiqatan "har qanday" tur kerak bo'lsa (masalan, generic cheklov) —
unknownyoki generic<T>.
3. Turni qayerda yozish kerak
3.1 Ortiqcha annotatsiya
const count: number = 0; // ortiqcha
const names: string[] = ["Osh", "Manti"]; // ortiqcha
const doubled = prices.map((p: number): number => p * 2); // ortiqchaTypeScript bularning hammasini o'zi chiqaradi (tur chiqarish). Ortiqcha tur — shovqin: kodni uzaytiradi va ba'zan xato qiladi (const status: string = "tayyor" — literal "tayyor" turini string ga kengaytirib yuboradi, literal turlar). typescript-eslint'ning no-inferrable-types qoidasi (stylistic to'plamida) birinchi qatorni ushlaydi.
3.2 Chegaralarda — ochiq yozing
Teskari qoida ham bor. Bu joylarda turni yozing:
| Joy | Nega |
|---|---|
| Eksport funksiyaning parametr va natijasi | boshqa fayllar shu imzoga tayanadi; tasodifiy o'zgarish darhol ko'rinadi |
Bo'sh boshlanadigan qiymat ([], null, new Map()) |
tur chiqarishga ma'lumot yo'q |
Tashqi ma'lumot (JSON.parse, fetch) |
unknown — tekshiruvga majburlash |
| Obyekt literal ma'lum shaklga mos bo'lishi kerak | satisfies yoki tur — ortiqcha/yetishmagan maydonni ushlaydi |
Qoida: ichkarida — TypeScript chiqarsin, chegarada — siz yozing. Build darsidagi isolatedDeclarations aynan "chegarada yoz" ni majburiy qiladi.
Tekshirib ko'ring:
function total(prices: number[]): number { … }vaconst total = (prices: number[]) => …— natija turini qayerda yozish kerak?
Javob
Agar funksiya eksport qilinsa (boshqa fayllar unga tayansa) — natija turini yozing: imzo hujjat bo'ladi. Funksiya ichidagi tasodifiy o'zgarish (masalan, return da undefined qaytib qolishi) imzoni jim o'zgartirib yubormaydi. Bunday holatda tsc funksiyaning o'zida xato beradi, uni chaqirgan o'nta faylda emas. Fayl ichidagi kichik yordamchi funksiyada esa chiqarish yetarli: o'qish oson, xato bo'lsa chaqiruv joyida baribir ko'rinadi.
4. as, ! va @ts-ignore — xatoni yashirish
4.1 as — va'da, tekshiruv emas
type Config = { tipRate: number; currency: string };
const config = { tipRate: 0.1 } as Config;
console.log(config.currency.toUpperCase());Konsolda:
TypeError: Cannot read properties of undefined (reading 'toUpperCase')as Config — "ishon, bu Config". currency yo'qligini tsc ko'rmadi, chunki biz uni ko'rmaslikka ko'ndirdik. Ikki to'g'ri yo'l:
type Config = { tipRate: number; currency: string };
const a: Config = { tipRate: 0.1 };
const b = { tipRate: 0.1 } satisfies Config;Kompilyator:
TS2741: Property 'currency' is missing in type '{ tipRate: number; }' but required in type 'Config'.
TS2741: Property 'currency' is missing in type '{ tipRate: number; }' but required in type 'Config'.TypeScript aytyapti (ikkala qatorda): "{ tipRate: number; } turida currency yo'q, Config da esa u majburiy".
: Config— tur annotatsiyasi: obyekt shu turga mos bo'lishi shart.satisfies Config— o'sha tekshiruv, lekin obyekt o'zining aniq (tor) turini saqlaydi (satisfies).vazifalardagialoqaMatnlarishunday yozilgan.
4.2 as qachon to'g'ri
as butunlay taqiqlanmagan. U to'g'ri joylar:
as const— turni toraytiradi, kengaytirmaydi (bu tekshiruvni o'chirmaydi).- Tekshiruvdan keyin,
tscbuni tushunolmaganda: API darsidagidata as Record<string, unknown>— obyekt ekani bir qator oldin tekshirilgan. - Kutubxona ichida, tashqi imzo aniq bo'lsa (DI konteyneri — Legacy dekoratorlar).
Har as yonida o'zingizga savol bering: "nega bunga ishonaman?" Javob — "chunki tepada tekshirdim" bo'lsa, yaxshi. "Chunki shunday bo'lishi kerak" bo'lsa — tekshiruv yozing.
4.3 ! va @ts-ignore
x!— "nullemas, ishon". DOM darsida ko'rdik: HTML o'zgarsa —TypeError. O'rniga —instanceofyokibyIdkabi tekshiruvchi.// @ts-ignore— xatoni jim o'chiradi, xato yo'qolsa ham jim qoladi. O'rniga —// @ts-expect-error — sabab(Ko'chish darsi).
4.4 ESLint — avtomatik nazoratchi
Bu odatlarni ESLint majburlaydi. Bitta "yomon" faylni typescript-eslint 8.71.1 (recommended + stylistic + no-non-null-assertion) ga berdik — haqiqiy chiqish (ustunlar qisqartirildi):
1:7 Type number trivially inferred from a number literal, remove type annotation no-inferrable-types
2:24 Unexpected any. Specify a different type no-explicit-any
5:15 Forbidden non-null assertion no-non-null-assertion
6:1 Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free ban-ts-comment
8:11 An empty interface declaration allows any non-nullish value … no-empty-object-typeHar qoida — shu darsdagi bitta anti-pattern. Ularni CI'da yuritsangiz, odat jamoa uchun majburiy bo'ladi. vazifalar da shulardan tashqari Promise qoidalari ham bor (Async kod).
Tekshirib ko'ring:
const user = JSON.parse(text) as User;— bu qatorda nechta anti-pattern bor?
Javob
Ikki: JSON.parse — any manbai, as User esa uni tekshiruvsiz turga aylantiradi. Natijada user ning turi chiroyli, lekin rost emas. To'g'risi: const data: unknown = JSON.parse(text); va parseUser(data) (yoki Zod sxemasi — Zod).
5. Haddan tashqari murakkab turlar
5.1 "Aqlli" tur — kimga kerak?
Mapped, conditional va template literal turlar bilan deyarli hamma narsani ifodalash mumkin. Lekin "mumkin" — "kerak" degani emas:
type Repeat<S extends string, N extends number, A extends S[] = []> =
A["length"] extends N ? "" : `${S}${Repeat<S, N, [...A, S]>}`;
type Line = Repeat<"-", 2000>;Kompilyator:
TS2589: Type instantiation is excessively deep and possibly infinite.Tarjimasi: "turni yaratish haddan tashqari chuqur va cheksiz bo'lishi mumkin" (tur murakkabligi chegaralari). Lekin muammo faqat chegara emas. Bunday tur muharrirni sekinlashtiradi, uning xato xabarlari o'qib bo'lmaydi, va uni yozgan odamdan boshqa hech kim o'zgartira olmaydi.
Qoidalar:
- Tur — hujjat. Agar uni tushuntirish uchun 10 qatorlik izoh kerak bo'lsa, ehtimol oddiyroq yechim bor: bir nechta oddiy tur, overload yoki ish vaqtidagi tekshiruv.
- Murakkab turni yozsangiz — unga test yozing (masalan,
@ts-expect-errorbilan "bu xato bo'lishi kerak" degan qatorlar) va nom bering. - Kutubxona muallifi bo'lsangiz, murakkab turlar oqlanishi mumkin (Zod'ning
z.inferi — bunga misol). Ilova kodida esa odatda oddiylik yutadi.
5.2 type-fest — tayyor utility turlar
Ko'p kerakli "aqlli" turlar allaqachon yozilgan va testlangan. type-fest (5.10.0) — shunday to'plam (faqat turlar, ish vaqtida 0 bayt):
import type { SetRequired, PartialDeep, JsonValue } from "type-fest";
type Guest = { name: string; phone?: string; note?: string };
type GuestWithPhone = SetRequired<Guest, "phone">; // phone majburiy
type Settings = { menu: { lang: string; theme: { dark: boolean } } };
const patch: PartialDeep<Settings> = {
menu: { theme: { dark: true } },
};
const cached: JsonValue = { items: [1, "osh", null] };SetRequired<T, K>— tanlangan ixtiyoriy xususiyatlarni majburiy qiladi.PartialDeep<T>—Partialning ichma-ich versiyasi (Utility types dagiPartialfaqat birinchi qavatni oladi).Tagged<number, "Sum">— brend tur: oddiynumberniSumdeb berib bo'lmaydi (Type 'number' is not assignable to type 'Sum'). O'z__brandmaydoningizni yozish o'rniga.JsonValue— "JSON'ga aylantirsa bo'ladigan qiymat".{ d: new Date() }ni bersangiz,tsc(biz sinadik):Type 'Date' is not assignable to type 'JsonValue | undefined'— sana JSON'da satr bo'lib qoladi.
O'zingiz yozishdan oldin type-fest'da bormi — qarang. Lekin har bitta tur uchun paket qo'shish shart emas: Partial, Pick, Omit, Record — TypeScript'ning o'zida.
5.3 ts-reset — standart turlarni tuzatish
TypeScript'ning standart kutubxonasida tarixiy sabablarga ko'ra bir nechta "yumshoq" joy bor. ts-reset (@total-typescript/ts-reset 0.6.1) ularni tuzatadi. Loyihaga bitta fayl qo'shiladi:
// reset.d.ts
import "@total-typescript/ts-reset";Shu kodni ts-reset'siz va u bilan tsc dan o'tkazdik:
const raw = JSON.parse('{"narx": 35000}');
const narx: string = raw.narx;
const prices: number[] = [35000, undefined, 28000].filter(Boolean);
const STATUSES = ["yangi", "tayyor"] as const;
const status: string = "tayyor";
console.log(narx, prices, STATUSES.includes(status));| ts-reset'siz | ts-reset bilan | |
|---|---|---|
JSON.parse |
any — narx: string ga son jim yozildi |
unknown — TS18046 |
.filter(Boolean) |
(number | undefined)[] — TS2322 |
number[] — xato yo'q |
includes(status) |
TS2345 (string — literal union emas) |
xato yo'q |
Eng muhimi — birinchi qator: ts-reset'siz tsc haqiqiy bugni ko'rmadi (son string o'zgaruvchiga tushdi), u bilan esa ko'rdi. Ikki xato yo'qoldi, bitta haqiqiy xato paydo bo'ldi — yaxshi almashinuv.
Diqqat: ts-reset global turlarni o'zgartiradi: loyihadagi hamma fayl, hatto
node_modulesdagi turlar ham buni ko'radi. Muallifi (Matt Pocock) tavsiyasi: faqat ilova kodida ishlating, boshqalar o'rnatadigan kutubxonada — yo'q.
6. Yaxshi odatlar — bir sahifada
15-qism bo'ylab yig'ilgan qoidalar:
| Mavzu | Qoida | Dars |
|---|---|---|
| Sozlama | strict + noUncheckedIndexedAccess |
Strict |
| Tashqi ma'lumot | unknown + tekshiruv yoki sxema |
API, Zod |
| Holatlar | discriminated union + never |
Discriminated union |
| Konstantalar | as const + literal union, enum emas |
enum |
| O'zgarmaslik | readonly parametr va maydonlar |
Massiv |
| DOM | instanceof, ! emas |
DOM |
| Xatolar | kutilgan — Result, bug — throw |
Async |
| Importlar | import type, verbatimModuleSyntax |
Modullar |
| Nazorat | tsc --noEmit + typescript-eslint CI'da |
Build |
Bitta jadval — butun qism. Bularning hammasi vazifalar v5 ning AGENTS.md faylida ham yozilgan: "any yo'q. as — oxirgi chora; DOM elementi instanceof bilan, catch'dagi xato unknown — toraytirib ishlating."
7. 15-qism xaritasi
47 dars — sakkiz blok. Har blok oldingisiga tayanadi:
flowchart TB
A["1. Kirish: 01–04<br/>tsc, annotatsiya, massiv"] --> B["2. Turlarni qurish: 05–10<br/>obyekt, union, funksiya"]
B --> C["3. Toraytirish: 11–16<br/>narrowing, type guard, null"]
C --> D["4. Lug'at, klass, enum<br/>17–19"]
D --> E["5. Generics: 20–24"]
E --> F["6. Tur darajasida: 25–32<br/>mapped, conditional, infer"]
F --> G["7. Sozlama va build: 33–40<br/>tsconfig, .d.ts, dekorator"]
G --> H["8. Amaliyot: 41–47<br/>DOM, API, Zod, ko'chish"]- Kirish — TypeScript xatoni ishga tushirmasdan topadi;
tsc7.0.2 va Node 24 type stripping; tur annotatsiyasi va tur chiqarish (inference); massiv, tuple,as const. - Turlarni qurish — obyekt turlari,
typevainterface, union va literal turlar,any/unknown/never, funksiya turlari va strukturaviy tiplash ("nomi emas, shakli"). - Toraytirish —
typeof,in,instanceofbilan toraytirish (narrowing), discriminated union va to'liqlik tekshiruvi, tur predikati (type guard) vaasserts,as/!/satisfies,nullbilan xavfsiz ishlash, kengaytirish va kesishma. - Lug'at, klass, enum — index signature va
Record, TypeScript klasslari,enumo'rnigaas const+ literal union. - Generics —
<T>, cheklov (extends,keyof), generic interfeys va klass,consttur parametri, overload vathis. - Tur darajasida dasturlash —
keyof/typeof/indexed access, utility types, mapped, conditional,infer, template literal, brend turlar va rekursiv turlar (va ularning chegarasi). - Sozlama va build —
tsconfig.json, strict bayroqlar, modullar vaimport type,.d.ts, kutubxona turlari,tsc/bundler/TS 7, standart va legacy dekoratorlar. - Amaliyot — DOM turlari, API javobi
unknown, Zod, async vaResult, JavaScript'dan ko'chish, xato xabarlarini o'qish va shu dars.
O'zingizni tekshiring — quyidagilarni qila olsangiz, qism o'zlashtirilgan:
tsc --noEmitxatosini o'qib, zanjirni pastdan yuqoriga tushuntira olaman.- Tashqi ma'lumotni
unknowndeb olib, tur predikati yoki sxema bilan tekshiraman. - Holatlarni discriminated union bilan yozaman va
neverbilan to'liqligini tekshiraman. - Generic funksiya yozaman va unga to'g'ri cheklov qo'yaman.
- Mavjud turdan
Pick,Partialyoki mapped tur bilan yangisini yasayman. tsconfig.jsonni o'qib,target,lib,moduleva strict bayroqlarini tushuntiraman.- JavaScript faylni
.tsga ko'chiraman:any,asva!siz.
8. 15-qism yakuni: vazifalar v5
flowchart TB
A["v4.1: JavaScript<br/>@ts-check"] --> B["tsc build<br/>allowJs"]
B --> C["modullar birma-bir .ts<br/>12 qadam"]
C --> D["v5: hamma modul .ts<br/>allowJs yo'q"]
D --> E["16-qism: Vite"]Bu qismda vazifalar JavaScript'dan TypeScript'ga to'liq o'tdi. Natija raqamlarda (biz har birini tekshirdik):
- 2 721 qator TypeScript (
assets/js/*.ts): birorta hamany,as(as constdan tashqari),!yoki@ts-direktivasi yo'q. tsc7.0.2:strict+ 7 qo'shimcha bayroq, 0 xato. Ko'chish davomida 383 tur xatosi topildi va tuzatildi — orasida haqiqiy buglar.- 143 test (
node --test,.tsni Node 24 o'zi o'qiydi), 159/159 brauzer tekshiruvi har qadamda, Lighthouse — 100 / 100 / 100 / 100. dist/dagi JavaScript — 81 KB: turlar o'chdi, foydalanuvchi TypeScript uchun bir bayt ham to'lamaydi.
Qolgan qarzlar ham yozilgan (TEXNIK-QARZ): sw.js hali JavaScript, ikki TypeScript paketi (TS 7.1 kutilmoqda), build — faqat tsc. Keyingi qism — frontend asboblari: npm chuqur, bundler'lar va Vite (Bundler nima). U yerda vazifalar bundler'ga o'tadi va Zod masalasi qayta ochiladi.
9. Ko'p uchraydigan xatolar
8.1 : any bilan "tez yechim"
Tuzatish: unknown + tekshiruv; ESLint no-explicit-any.
8.2 Har qatorga tur
Tuzatish: ichkarida chiqarishga ishoning; chegarada yozing. ESLint no-inferrable-types.
8.3 as bilan xatoni yo'qotish
Tuzatish: annotatsiya, satisfies yoki tekshiruv. as — faqat "tepada tekshirdim" bo'lsa.
8.4 TS2589 — tur juda chuqur
Tuzatish: turni soddalashtiring yoki ish vaqtidagi tekshiruvga o'tkazing.
8.5 ts-reset'ni kutubxonaga qo'shish
Tuzatish: faqat ilovada.
10. Mashqlar
1-mashq (oson): Anti-patternni toping
function load(): any { return JSON.parse(localStorage.getItem("savat")!); } — bu qatorda nechta anti-pattern bor?
Yechim
Ikkita: : any (natija tekshiruvsiz tarqaladi; to'g'risi — unknown va tekshiruv) va ! (getItem kalit yo'q bo'lsa null qaytaradi; JSON.parse(null) esa null beradi — keyingi kodda kutilmagan null). To'g'ri variant: const text = localStorage.getItem("savat"); if (text === null) return EMPTY; va parseCart(text).
2-mashq (o'rta): satisfies bilan menyu
Menyu obyekti har taomga narx bersin; taom nomlari turi obyektdan chiqarilsin (keyof typeof), har narx — son. as ishlatmang. Ishora: satisfies Record<string, number> — narxlar tekshiriladi, lekin kalitlar aniq qoladi.
Yechim
const MENU = {
osh: 35000,
lagmon: 28000,
manti: 30000,
choy: 5000,
} satisfies Record<string, number>;
type Dish = keyof typeof MENU; // "osh" | "lagmon" | "manti" | "choy"
function priceOf(dish: Dish): number {
return MENU[dish];
}
console.log(priceOf("manti")); // 30000: Record<string, number> deb yozsak, keyof typeof MENU — oddiy string bo'lardi va priceOf("somsa") ham o'tardi. satisfies ikkala ishni qildi: narxlar son ekanini tekshirdi va kalitlarni aniq saqladi. as Record<…> esa umuman tekshirmasdi.
3-mashq (qiyin): Amaliy tajriba — "tez yozilgan" savatni tozalash
kurs/mashqlar/15/47-amaliyot/cart-legacy.ts — tsc dan xatosiz o'tadigan, lekin buzuq kod:
// 47-amaliyot/cart-legacy.ts — "tez yozilgan" savat
export function addItem(cart: any, item: any) {
cart.items.push(item);
cart.total = cart.total + item.price * item.qty;
return cart;
}
export function fromStorage(text: string) {
return JSON.parse(text) as { items: any[]; total: number };
}
export function discount(cart: any, code: string) {
// @ts-ignore
const percent: number = { BAHOR10: 10, CHOY5: 5 }[code];
return Math.round(cart.total * (1 - percent! / 100));
}Biz sinadik: discount(cart, "bahor10") (kichik harf) — NaN; keshdan "total": "70000" (satr) kelgan savatga 5 000 so'mlik choy qo'shilsa — total "700005000". Beshta anti-patternni toping va cart.ts da toza versiya yozing: aniq turlar (readonly), yangi savat qaytaradigan addItem, Result qaytaradigan discount, keshni tekshiradigan parseCart. cart.test.ts — uch test.
Yechim
Anti-patternlar: (1) any parametrlar; (2) addItem savatni joyida o'zgartiradi (mutatsiya); (3) JSON.parse(…) as … — tekshiruvsiz kesh; (4) @ts-ignore; (5) percent! — undefined bo'lishi mumkin bo'lgan qiymatga "ishon".
// 47-amaliyot/cart.ts — savat: aniq turlar, tekshiruv, Result
export type CartItem = {
readonly name: string;
readonly price: number;
readonly qty: number;
};
export type Cart = {
readonly items: readonly CartItem[];
readonly total: number;
};
export type Result<T, E> =
| { readonly ok: true; readonly value: T }
| { readonly ok: false; readonly error: E };
export const EMPTY_CART: Cart = { items: [], total: 0 };
// Yangi savat qaytaradi — eskisi o'zgarmaydi
export function addItem(cart: Cart, item: CartItem): Cart {
return {
items: [...cart.items, item],
total: cart.total + item.price * item.qty,
};
}
const DISCOUNTS = { BAHOR10: 10, CHOY5: 5 } satisfies Record<
string,
number
>;
type DiscountCode = keyof typeof DISCOUNTS;
function isDiscountCode(code: string): code is DiscountCode {
return Object.hasOwn(DISCOUNTS, code);
}
export function discount(
cart: Cart,
code: string,
): Result<number, string> {
if (!isDiscountCode(code)) {
return { ok: false, error: `Noma'lum kod: ${code}` };
}
const value = Math.round(cart.total * (1 - DISCOUNTS[code] / 100));
return { ok: true, value };
}
function isItem(value: unknown): value is CartItem {
return (
typeof value === "object" &&
value !== null &&
"name" in value &&
typeof value.name === "string" &&
"price" in value &&
typeof value.price === "number" &&
"qty" in value &&
Number.isInteger(value.qty)
);
}
// Kesh — tashqi ma'lumot: tekshiriladi, total qayta hisoblanadi
export function parseCart(text: string): Cart {
const data: unknown = JSON.parse(text);
if (typeof data !== "object" || data === null) {
throw new TypeError("Savat formati buzuq");
}
if (!("items" in data)) {
throw new TypeError("Savatda taomlar yo'q");
}
const { items } = data;
if (!Array.isArray(items) || !items.every(isItem)) {
throw new TypeError("Savatdagi taomlar buzuq");
}
return items.reduce<Cart>(addItem, EMPTY_CART);
}isDiscountCode— tur predikati:Object.hasOwndan keyincode—"BAHOR10" | "CHOY5",DISCOUNTS[code]—number. Na@ts-ignore, na!.parseCartkeshdagitotalga ishonmaydi — uni taomlardan qayta hisoblaydi. Kesh — tashqi ma'lumot.items.every(isItem)—everyning tur predikatli shakli: tekshiruvdan keyinitems—CartItem[].items.reduce<Cart>(addItem, EMPTY_CART)—addItem(cart, item)imzosireducening callback'iga to'g'ri keladi.
cart.test.ts:
import { test } from "node:test";
import assert from "node:assert/strict";
import { EMPTY_CART, addItem, discount, parseCart } from "./cart.ts";
const OSH = { name: "Osh", price: 35000, qty: 2 };
test("addItem: yangi savat, eskisi o'zgarmaydi", () => {
const cart = addItem(EMPTY_CART, OSH);
assert.equal(cart.total, 70000);
assert.equal(EMPTY_CART.items.length, 0);
});
test("discount: ma'lum va noma'lum kod", () => {
const cart = addItem(EMPTY_CART, OSH);
const result = discount(cart, "BAHOR10");
assert.deepEqual(result, { ok: true, value: 63000 });
assert.equal(discount(cart, "bahor10").ok, false);
});
test("parseCart: total keshdan emas, taomlardan hisoblanadi", () => {
const text = JSON.stringify({ items: [OSH], total: "70000" });
assert.equal(parseCart(text).total, 70000);
const broken = '{"items":[{"name":"Osh"}]}';
assert.throws(() => parseCart(broken), TypeError);
});npx tsc --pretty false — toza, node --test 47-amaliyot/cart.test.ts — bizda (Node 24.21.0):
✔ addItem: yangi savat, eskisi o'zgarmaydi (2.414ms)
✔ discount: ma'lum va noma'lum kod (2.7245ms)
✔ parseCart: total keshdan emas, taomlardan hisoblanadi (1.798ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 328.514Ikki versiya ham tsc dan xatosiz o'tadi — farq shundaki, birinchisi tsc ni aldaydi, ikkinchisi unga tekshirishga imkon beradi. XATOLAR.md ga bu darsning yangi xabarini (TS2589) qo'shing va daftarni yakunlang.
git add 15/47-amaliyot 15/XATOLAR.md
git commit -m "15/47: savat — any, as va ! siz; testlar"11. Real ishda
- Kod sharhi (code review) —
any,as,!,@ts-ignore— har biri savol tug'diradi: "nega?". Ko'p jamoalarda ular ESLint orqali taqiqlangan, istisno — izohlieslint-disablebilan. - Ishga kirganingizda ko'pincha "yarim TypeScript" loyihaga duch kelasiz:
anylar, eski@ts-ignorelar. Bu darsdagi tartib — avval chegaralar (unknown), keyinaslar, oxirida ESLint qoidalarini yoqish — amaliy reja. - Intervyu: "
anyvaunknownfarqi?", "asvasatisfiesfarqi?", "Turni qachon yozasiz, qachon chiqarishga qoldirasiz?", "ts-reset nima qiladi?".
Xulosa
anyyuqumli — tekshiruvni butun zanjir bo'ylab o'chiradi. Chegaradaunknown, ESLint'dano-explicit-any.- Ichkarida — tur chiqarish, chegarada (eksport, bo'sh qiymat, tashqi ma'lumot) — ochiq tur. Ortiqcha annotatsiya — shovqin.
as,!,@ts-ignore— xatoni yashiradi; o'rniga annotatsiya,satisfies,instanceof,@ts-expect-error.- Murakkab tur — faqat oqlansa;
type-fest— tayyor utility turlar,ts-reset— ilovada standart turlarni qattiqlashtiradi. vazifalarv5: 2 721 qator TypeScript, 0any, 0as, 143 test, 159/159 — 15-qism shu bilan yakunlandi.
Keyingi dars: Frontend asboblar zanjiri — 16-qism boshlanadi: manba koddan brauzergacha — npm, bundler'lar, transpilyatorlar va ular vazifalar ni qanday o'zgartirishi.
Manbalar
- TypeScript Handbook: "Do's and Don'ts" — typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html
- typescript-eslint: shared configs (
recommended,stylistic,strict) — typescript-eslint.io/users/configs - type-fest — github.com/sindresorhus/type-fest (5.10.0)
- ts-reset — totaltypescript.com/ts-reset (
@total-typescript/ts-reset0.6.1)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!