Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. Template literal type: shablondan union
- 2.1 Yozuv va hisoblash
- 2.2 Ikki union — hamma juftliklar
- 2.3 Qachon template literal, qachon oddiy union
- 3. Harflarni o'zgartiradigan tayyor turlar
- 4. Mapped type bilan: onCreated, onReady
- 5. Naqsh turlari: ${number} va ${string}
- 5.1 "Shu shakldagi istalgan satr"
- 5.2 ${number} aslida nimani qabul qiladi?
- 6. infer bilan satrni bo'lish
- 7. Qiymatlar dunyosida: shablon satr qaysi tur oladi
- 8. TypeScript ushlamaydigan tuzoq: tur va satr bir-biridan ajralib qolsa
- 8.1 as bilan yolg'on nom
- 8.2 Naqsh — shakl, ma'no emas
- 9. Ko'p uchraydigan xatolar
- 9.1 Ro'yxatda yo'q nom
- 9.2 Harf turiga satr bo'lmagan narsa
- 9.3 as siz Capitalize natijasini qaytarish
- 9.4 Juftliklar portlashi
- 9.5 Naqshga ortiqcha ishonish
- 10. Mashqlar
- 1-mashq (oson): Kombinatsiyalarni sanang
- 2-mashq (o'rta): CSS o'zgaruvchilari va getter'lar
- 3-mashq (qiyin): Amaliy tajriba — oshxona hodisalari
- 11. Real ishda
- Xulosa
- Manbalar
TypeScript template literal types: satr turlarini shablon bilan yasash
Qisqacha: Template literal type — JavaScript'dagi shablon satr (
`on${name}`), lekin turlar bilan:`order:${OrderEvent}`— "order:bilan boshlanib, hodisa nomi bilan tugaydigan satr". Union qo'yilsa, hamma kombinatsiyalar chiqadi.Capitalize,Uppercasekabi tayyor turlar harflarni o'zgartiradi,inferesa satrni bo'laklarga ajratadi. Hammasi faqat kompilyatsiyada ishlaydi: ishlash paytidagi satrni siz o'zingiz yasaysiz va tekshirasiz.
Bu darsda
`order:${OrderEvent}`kabi tur yozasiz va kompilyator undan qanday union yasashini ko'rsata olasiz.Uppercase,Lowercase,Capitalize,Uncapitalizeni ishlatasiz.- Mapped type bilan
onCreated,onReadykabi kalitlar yasaysiz. `${number}:${number}`kabi naqsh turlari nimani tekshirishini va nimani tekshirmasligini bilasiz.inferbilan satr turini bo'laklarga ajratasiz:"/menu/:id"→"id".
Oldin bilishingiz kerak: infer kalit so'zi, Mapped types, Shablon satrlar.
1. Nega bu kerak?
O'tgan darsda infer bilan turning ichiga kirdik. Bugun yana bir "ichki dunyo" — satr turlari. TypeScript satrning ichini ham ko'ra oladi: qaysi so'z bilan boshlanadi, nimadan iborat.
«Bahor» oshxona ekrani hodisalar bilan ishlaydi: buyurtma yaratildi, tayyor bo'ldi, mijozga berildi. Har hodisaning nomi — satr: "order:created", "order:ready", "order:served". Sardor hodisa yuboradigan funksiya yozdi:
const listeners = new Map<string, () => void>();
listeners.set("order:ready", () => {
console.log("Ofitsiant chaqirildi");
});
function emit(name: string) {
listeners.get(name)?.();
}
emit("order:ready"); // Ofitsiant chaqirildi
emit("order:redy");Ikkinchi chaqiruvda harf tushib qolgan. JavaScript ham, string turi ham indamadi — hodisa shunchaki hech kimga yetmadi. Ofitsiant chaqirilmadi, osh soviyapti.
Sardor turni literal union qildi: "order:created" | "order:ready" | "order:served". Xato ushlanadi. Lekin bir hafta o'tib stollar uchun ham hodisalar kerak bo'ldi: "table:free", "table:busy". Keyin har hodisa uchun ishlovchi nomi: "onCreated", "onReady"… Har ro'yxat qo'lda yoziladi va mapped types darsidagi takrorlanish muammosi yana qaytadi: yangi hodisa — uch joyda tahrir.
Template literal type nomlarni bitta manbadan yasaydi:
type OrderEvent = "created" | "ready" | "served";
type EventName = `order:${OrderEvent}`;
const ev: EventName = "order:cancelled";
const ev2: EventName = "order:Ready";Kompilyator:
TS2322: Type '"order:cancelled"' is not assignable to type '"order:created" | "order:ready" | "order:served"'.
TS2820: Type '"order:Ready"' is not assignable to type '"order:created" | "order:ready" | "order:served"'. Did you mean '"order:ready"'?- Birinchisi:
"order:cancelled"— ro'yxatda yo'q hodisa. - Ikkinchisi yangi kod —
TS2820: xuddi shu xabar, oxirida esa ""order:ready"ni nazarda tutdingizmi?". Kompilyator kattaRni sezdi va o'xshash variantni taklif qildi.
E'tibor bering: xabarda EventName emas, uning hisoblangan ko'rinishi — uchta satrning union'i. OrderEvent ga "cancelled" qo'shsangiz, EventName o'zi to'rtta bo'ladi.
2. Template literal type: shablondan union
2.1 Yozuv va hisoblash
Template literal type (shablon satr turi) — backtick ichida yozilgan tur: oddiy matn va ${…} ichida boshqa turlar. Yozuvi shablon satrlar bilan bir xil, faqat ${…} ichida qiymat emas, tur turadi.
type OrderEvent = "created" | "ready" | "served";
type EventName = `order:${OrderEvent}`;
const ev: EventName = "order:ready";
console.log(ev); // order:readyKompilyator EventName ni shunday ochadi:
| Qadam | Nima qiladi | Natija |
|---|---|---|
| 1 | ${…} ichidagi union a'zolarini oladi |
"created", "ready", "served" |
| 2 | har biriga shablonni qo'llaydi | "order:created", "order:ready", "order:served" |
| 3 | natijalarni union'ga yig'adi | uch satrli union |
Bu conditional type'dagi taqsimlanish ga o'xshaydi: union a'zolari bittalab ishlanadi.
Muhimi — natija oddiy literal union. Undan keyin EventName bilan qo'lda yozilgan "order:created" | "order:ready" | "order:served" o'rtasida hech qanday farq yo'q: switch bilan toraytirish, to'liqlik tekshiruvi, Record<EventName, …> — hammasi ishlaydi. Template literal type yangi turdagi tur emas, union yasashning qisqa yo'li.
2.2 Ikki union — hamma juftliklar
${…} ikkita bo'lsa, kompilyator hamma juftliklarni yasaydi. «Bahor» menyusida porsiya kalitlari — taom va o'lcham:
type Dish = "osh" | "manti";
type Size = "kichik" | "katta";
type PortionKey = `${Dish}-${Size}`;
const key: PortionKey = "manti-katta";
console.log(key); // manti-kattaHover: type PortionKey = "osh-kichik" | "osh-katta" | "manti-kichik" | "manti-katta". Ikki × ikki = to'rt. Rasmda:
flowchart LR
D1["osh"] --> K1["osh-kichik"]
D1 --> K2["osh-katta"]
D2["manti"] --> K3["manti-kichik"]
D2 --> K4["manti-katta"]
S1["kichik"] --> K1
S1 --> K3
S2["katta"] --> K2
S2 --> K4Kunlik o'xshatish: kiyim do'konidagi yorliqlar. Uch xil rang va to'rt xil o'lcham bo'lsa — o'n ikki xil yorliq bosiladi, har biri alohida.
Diqqat: juftliklar soni ko'paytma bilan o'sadi. 10 ta taom × 3 o'lcham × 4 qo'shimcha = 120 ta satr. Ikki xonali raqam (
${0|1|…|9}${0|1|…|9}) — 100 ta. Kombinatsiyalar 100 000 ga yetsa, kompilyator hisoblashdan bosh tortadi va xato beradi (TS2590). Bu chegarani Rekursiv turlar va tur murakkabligi chegaralari darsida ko'ramiz.
2.3 Qachon template literal, qachon oddiy union
Template literal type'ni qoida bo'lganda yozing: "hamma buyurtma hodisalari order: bilan boshlanadi", "har hodisaning ishlovchisi on + hodisa nomi". Qoida turga yozilsa, uni buzib bo'lmaydi va yangi a'zo qo'shish bir joyda bo'ladi.
Qoida bo'lmasa — oddiy union yaxshiroq. Masalan, to'lov usullari: "naqd" | "karta" | "Click" | "Payme". Ularni shablon bilan yig'ishning ma'nosi yo'q: umumiy qism yo'q, har biri o'z nomi. Shablonga majburlash faqat o'qishni qiyinlashtiradi.
Tekshirib ko'ring:
type Table = `${1 | 2 | 3}-stol`— nechta satr va qaysilari?
Javob
Uchta: "1-stol" | "2-stol" | "3-stol". Shablon ichida son literal ham ishlaydi — u matnga aylanadi, xuddi JavaScript'dagi `${1}-stol` kabi.
3. Harflarni o'zgartiradigan tayyor turlar
TypeScript'da satr turining harflarini o'zgartiradigan to'rtta tayyor tur bor:
| Tur | Nima qiladi | Misol |
|---|---|---|
Uppercase<S> |
hammasi katta | "osh" → "OSH" |
Lowercase<S> |
hammasi kichik | "OSH" → "osh" |
Capitalize<S> |
birinchi harf katta | "ready" → "Ready" |
Uncapitalize<S> |
birinchi harf kichik | "Osh" → "osh" |
Ular lib.es5.d.ts da odatdagidek yozilmagan — type Capitalize<S extends string> = intrinsic;. intrinsic (ichki) — "bu turni kompilyatorning o'zi hisoblaydi" degani. Harflarni o'zgartirishni mapped yoki conditional type bilan yozib bo'lmaydi, shuning uchun u kompilyatorga qurilgan.
type OrderEvent = "created" | "ready";
type HandlerName = `on${Capitalize<OrderEvent>}`;
const h: HandlerName = "onReady";
const level: Capitalize<"o'rta"> = "O'rta";
console.log(h, level); // onReady O'rtaHandlerName — "onCreated" | "onReady". Avval Capitalize union'ning har a'zosiga qo'llandi, keyin shablon. Capitalize<"o'rta"> — "O'rta": birinchi belgi o katta bo'ldi, apostrof joyida qoldi.
Bu turlar faqat kompilyatsiyada ishlaydi. Ishlash paytida ularning hech qanday izi yo'q: "ready" ni "Ready" ga aylantirish uchun JavaScript'da charAt(0).toUpperCase() kabi kodni baribir o'zingiz yozasiz. Tur faqat "natija shunday bo'lishi kerak" deb aytadi — buni «Qiymatlar dunyosida» bo'limida batafsil ko'ramiz.
Yana bir nozik joy: Capitalize faqat birinchi belgini o'zgartiradi. "ko'k choy" → "Ko'k choy", "Ko'k Choy" emas. Har so'zni katta harf bilan boshlaydigan tayyor tur yo'q.
Turlarga faqat satr beriladi:
type Bad = Capitalize<35000>;Kompilyator:
TS2344: Type 'number' does not satisfy the constraint 'string'."number turi string cheklovini qanoatlantirmaydi". Capitalize<S extends string> — generic cheklov. Xabarda 35000 emas, number turibdi — kompilyator cheklovni tekshirishda son literalini umumiy number deb ko'rsatdi.
4. Mapped type bilan: onCreated, onReady
Mapped types darsida kalitni as bilan qayta nomlagan edik va shablon yozuvini "keyinroq" deb qoldirgandik. Mana o'sha joy:
type KitchenEvents = { created: number; ready: string };
type Handlers<T> = {
[K in keyof T as `on${Capitalize<K & string>}`]: (
value: T[K],
) => void;
};
const handlers: Handlers<KitchenEvents> = {
onCreated: (table) => console.log(`${table}-stol: yangi buyurtma`),
onReady: (dish) => console.log(`${dish} tayyor`),
};
handlers.onReady("Osh"); // Osh tayyorK |
Yangi kalit | Qiymat turi |
|---|---|---|
"created" |
"onCreated" |
(value: number) => void |
"ready" |
"onReady" |
(value: string) => void |
KitchenEvents — "hodisa → u bilan keladigan ma'lumot" jadvali. Ishlovchilar obyekti undan hisoblandi: kalit nomlari ham, parametr turlari ham. onCreated ichida table — number, onReady ichida dish — string. Yangi hodisa qo'shilsa, handlers da uning ishlovchisi yo'qligini tsc darhol aytadi.
K & string — mapped types darsidagi qoida: kalit symbol bo'lishi ham mumkin, uni shablonga qo'yib bo'lmaydi.
Tekshirib ko'ring:
KitchenEventsgaserved: numberqo'shildi,handlersesa o'zgarmadi.tscnima deydi?
Javob
TS2741 — "onServed xususiyati yo'q, lekin Handlers<KitchenEvents> turida majburiy". Yangi hodisa kalitdan ishlovchi nomi yasaldi va u majburiy maydonga aylandi. Ishlovchini yozmaguningizcha kod tekshiruvdan o'tmaydi — unutilgan hodisa bo'lmaydi.
5. Naqsh turlari: ${number} va ${string}
5.1 "Shu shakldagi istalgan satr"
${…} ichiga aniq satr emas, umumiy tur qo'ysa ham bo'ladi. Bron vaqti — "son, ikki nuqta, son":
type Time = `${number}:${number}`;
const ok: Time = "19:30";
const bad: Time = "19.30";Kompilyator:
TS2322: Type '"19.30"' is not assignable to type '`${number}:${number}`'.Tarjimasi: ""19.30" turini `${number}:${number}` turiga berib bo'lmaydi" — nuqta bor, ikki nuqta yo'q. Bunday tur union emas — u naqsh (pattern): cheksiz ko'p satrlarga mos keladi. ${number} — "son bo'lib o'qiladigan matn", ${string} — "istalgan matn" (bo'sh ham).
${string} prefiks yoki qo'shimcha qoidasi uchun qulay. «Bahor» jurnaliga (log) har qanday buyurtma hodisasi yozilishi mumkin — hatto hali OrderEvent da yo'q yangisi ham. Faqat order: bilan boshlanishi shart:
type AnyOrderEvent = `order:${string}`;
function logOrder(name: AnyOrderEvent): string {
return `[jurnal] ${name}`;
}
console.log(logOrder("order:refund")); // [jurnal] order:refund"order:refund" — OrderEvent da yo'q, lekin naqshga mos. logOrder("table:free") esa tsc dan o'tmaydi: prefiks boshqa. Aniq union — "faqat shu ro'yxat", naqsh — "shu qoidaga mos istalgani". Qaysi biri kerakligini vaziyat belgilaydi: hodisa ishlovchisi aniq ro'yxatni talab qiladi (har biriga kod yozilgan), jurnal esa qoidaning o'zi bilan kifoyalanadi.
5.2 ${number} aslida nimani qabul qiladi?
Bu yerda ehtiyot bo'ling. TypeScript 7.0.2 da tekshirib ko'rdik:
| Satr | ${number} ga mosmi? |
Nega |
|---|---|---|
"35000", "-5.5" |
ha | oddiy son |
"1e3", "0x1F", "5." |
ha | JavaScript ularni son deb o'qiydi |
" 5" |
ha | boshdagi bo'shliq ham |
"", "35 000", "NaN", "Infinity" |
yo'q | — |
Ya'ni ${number} — "JavaScript Number() bilan son sifatida o'qiy oladigan matn", "chiroyli raqam" emas. "0x1F" — o'n oltilik son, Number("0x1F") — 31. Turdan o'tdi, lekin «Bahor» narxi sifatida ma'nosiz.
Tekshirib ko'ring:
type Price = `${number} so'm`turiga"35000 so'm","35 000 so'm"va"so'm"dan qaysilari mos?
Javob
Faqat "35000 so'm". "35 000 so'm" da ${number} o'rniga "35 000" tushadi — bo'shliqli raqam son emas. "so'm" da esa ${number} o'rniga bo'sh satr qoladi, bo'sh satr ham son emas. (Shablondagi so'm oldidagi bo'shliq ham hisobga olinadi.)
6. infer bilan satrni bo'lish
O'tgan darsdagi qolip g'oyasi satrlarda ham ishlaydi. Qolip — shablon, bo'sh joylar — infer:
type Split<T> = T extends `${infer Dish}-${infer Size}`
? [dish: Dish, size: Size]
: never;
const parts: Split<"osh-katta"> = ["osh", "katta"];
console.log(parts[1]); // katta| Qadam | Nima qiladi | Natija |
|---|---|---|
| 1 | "osh-katta" ni ${…}-${…} qolipiga soladi |
- dan oldin va keyin |
| 2 | birinchi infer — birinchi - gacha |
Dish = "osh" |
| 3 | ikkinchi infer — qolgan hammasi |
Size = "katta" |
Uch bo'lakli satrda ("osh-katta-achchiq") natija — ["osh", "katta-achchiq"]. Birinchi infer imkon qadar qisqa bo'lakni oladi (birinchi - gacha), oxirgisi — qolganini.
Yo'l (route) parametri — veb-ilovalarda eng mashhur misol:
type ParamOf<T> = T extends `${string}:${infer P}` ? P : never;
const p: ParamOf<"/menu/:id"> = "id";
console.log(p); // id${string} — "nima bo'lsa ham" (bu yerda /menu/), : dan keyingisi P bo'ladi. Ikki va undan ko'p parametr ("/menu/:dishId/size/:size") uchun tur o'z-o'zini chaqirishi kerak — buni Rekursiv turlar darsida yozamiz.
Taqsimlanish bu yerda ham ishlaydi — prefiksni olib tashlash:
type WithoutPrefix<T> = T extends `order:${infer Rest}`
? Rest
: never;
type All = "order:created" | "order:ready" | "table:free";
const e: WithoutPrefix<All> = "ready";
console.log(e); // readyKompilyator All ni a'zolarga bo'lib, har birini qolipga soladi:
| A'zo | order:${infer Rest} ga tushdimi? |
Natija |
|---|---|---|
"order:created" |
ha, Rest = "created" |
"created" |
"order:ready" |
ha, Rest = "ready" |
"ready" |
"table:free" |
yo'q | never — o'chadi |
Yig'indi: "created" | "ready".
Tekshirib ko'ring:
Split<"osh">(defissiz) nima beradi?ParamOf<"/menu">chi?
Javob
Ikkalasi ham never. "osh" da - yo'q — ${infer Dish}-${infer Size} qolipiga tushmaydi. "/menu" da : yo'q — parametr qolipiga tushmaydi. O'tgan darsdagi "jim never" qoidasi satrlarda ham amal qiladi: qolip mos kelmasa, xato emas — : shoxi.
7. Qiymatlar dunyosida: shablon satr qaysi tur oladi
Turlar bilan ishlash bir narsa, haqiqiy satrni yasash — boshqa. JavaScript shablon satri qanday tur oladi?
| Kod | Hover'da |
|---|---|
const x = `on${"ready"}` |
"onready" |
let y = `on${"ready"}` |
string |
const w = `on${e}` as const (e: string) |
`on${string}` |
const bilan va qismlari aniq bo'lsa — aniq literal; let bilan — kengaygan string. as const esa naqsh turini saqlaydi.
Muammo — harf o'zgartirish. toUpperCase() natijasi — oddiy string, kompilyator Capitalize ni ishlash paytidagi kod bilan bog'lay olmaydi:
function handlerName<E extends string>(
event: E,
): `on${Capitalize<E>}` {
return `on${event.charAt(0).toUpperCase()}${event.slice(1)}`;
}Kompilyator:
TS2322: Type '`on${string}${string}`' is not assignable to type '`on${Capitalize<E>}`'."`on${string}${string}` turini `on${Capitalize<E>}` turiga berib bo'lmaydi". Kompilyator ko'rgani: "on + qandaydir satr + qandaydir satr". Birinchi harf haqiqatan katta bo'ldimi — u tekshira olmaydi.
Nega? TypeScript metodlarning ishini bilmaydi, faqat imzosini biladi. toUpperCase() imzosi — (): string: "satr qaytaraman". Qaysi satr — buni bilish uchun kodni bajarish kerak, kompilyator esa kodni bajarmaydi. Yana bir sabab: Capitalize<E> ni hisoblash uchun E aniq bo'lishi kerak, funksiya ichida esa u hali noma'lum.
Bu — Conditional types darsidagi holatning o'zi: natijani faqat as bilan berish mumkin. Bu as esa — tekshiruvsiz joy.
8. TypeScript ushlamaydigan tuzoq: tur va satr bir-biridan ajralib qolsa
8.1 as bilan yolg'on nom
Sardor handlerName ni as bilan yozdi — va Capitalize ni qilishni unutdi:
type OrderEvent = "created" | "ready";
type Handlers = {
[E in OrderEvent as `on${Capitalize<E>}`]: (t: number) => string;
};
const handlers: Handlers = {
onCreated: (table) => `${table}-stol: yangi buyurtma`,
onReady: (table) => `${table}-stol: tayyor`,
};
function handlerName<E extends OrderEvent>(event: E) {
return `on${event}` as `on${Capitalize<E>}`;
}
const name = handlerName("ready");
console.log(handlers[name](5));tsc — xatosiz. Node esa:
TypeError: handlers[name] is not a functionTurga ko'ra name — "onReady". Haqiqatda — "onready", kichik r bilan. handlers["onready"] — undefined, uni chaqirib bo'lmaydi. Template literal type — va'da, as esa uni tekshirmasdan qabul qildi.
Qoida: tur darajasidagi Capitalize, Uppercase ning ishlash paytidagi egizagi bo'lgan funksiyani bir marta yozing va test bilan tekshiring (mashqdagi kabi). Keyin hamma joyda shu funksiyani ishlating.
8.2 Naqsh — shakl, ma'no emas
type Time = `${number}:${number}`;
function toMinutes(time: Time): number {
const [hour = 0, minute = 0] = time.split(":").map(Number);
return hour * 60 + minute;
}
function isBookable(time: Time): boolean {
const m = toMinutes(time);
return m >= 10 * 60 && m <= 22 * 60;
}
console.log(isBookable("19:30")); // true
console.log(isBookable("9:75")); // true"9:75" — 9 soat 75 daqiqa. Bunday vaqt yo'q, lekin turdan o'tdi va hisob uni 10:15 deb oldi — "bron mumkin". Naqsh turi ${number}:${number} faqat shaklni tekshiradi: "son, ikki nuqta, son". 0–23 oralig'i, ikki xonali daqiqa — bular qiymat haqidagi qoidalar, ularni turlar emas, ishlash paytidagi kod tekshiradi. Mashqda shunday tekshiruvni yozasiz.
Xulosa — turlar va tekshiruvlar bir-birini to'ldiradi. Tur dasturchining xatosini ushlaydi: kodda "19.30" yozilsa, tsc darhol to'xtatadi. Ishlash paytidagi tekshiruv esa foydalanuvchining xatosini ushlaydi: mehmon formaga 9:75 yozsa, u turdan o'tgan bo'lsa ham rad etiladi. Ikkinchisini birinchisi bilan almashtirib bo'lmaydi — forma ma'lumoti tsc ishlaganda hali mavjud emas.
9. Ko'p uchraydigan xatolar
9.1 Ro'yxatda yo'q nom
TS2322 yoki TS2820 ("Did you mean …?") — hodisa nomida xato. Tuzatish: taklif qilingan variant yoki manba union'ga yangi a'zo.
9.2 Harf turiga satr bo'lmagan narsa
TS2344: Type 'number' does not satisfy the constraint 'string'. Tuzatish: Capitalize<K & string> yoki Capitalize<`${N}`> — avval matnga aylantiring.
9.3 as siz Capitalize natijasini qaytarish
TS2322: Type '`on${string}${string}`' is not assignable to type '`on${Capitalize<E>}`'. Tuzatish: as + test, yoki natija turini oddiyroq qiling.
9.4 Juftliklar portlashi
Uch-to'rt union'ni bitta shablonga qo'yish — yuzlab, minglab satr. Muharrir sekinlashadi, xabarlar o'qib bo'lmas holga keladi. Tuzatish: kombinatsiyalarni turga emas, ma'lumotga qo'ying ({ dish: Dish; size: Size } obyekti).
9.5 Naqshga ortiqcha ishonish
${number} — "0x1F" ni ham, "9:75" dagi 75 ni ham o'tkazadi. Tuzatish: ishlash paytida tekshiruv (RegExp yoki oraliq).
10. Mashqlar
1-mashq (oson): Kombinatsiyalarni sanang
type Drink = "choy" | "kofe";
type Temp = "issiq" | "sovuq";
type DrinkKey = `${Drink}-${Temp}`;
const d: DrinkKey = "choy-sovuq";
const loud: Uppercase<"choy"> = "CHOY";
console.log(d, loud); // choy-sovuq CHOYDrinkKey da nechta satr bor? [:4]. Capitalize<"kofe"> — "Kofe". Uppercase<"choy"> — [:CHOY].
Yechim
Ikki ichimlik × ikki harorat = 4: "choy-issiq", "choy-sovuq", "kofe-issiq", "kofe-sovuq". Uppercase hamma harfni katta qiladi — "CHOY"; Capitalize faqat birinchisini.
2-mashq (o'rta): CSS o'zgaruvchilari va getter'lar
- «Bahor» saytidagi mavzu ranglari:
ThemeColor = "asosiy" | "fon" | "matn".CssVar—"--rang-asosiy"kabi CSS o'zgaruvchisi nomlari (CSS o'zgaruvchilari dagi--prefiks). Getters<T>— har maydon uchungetName,getPricekabi funksiya: parametrsiz, maydon turini qaytaradi.
Yechim
type ThemeColor = "asosiy" | "fon" | "matn";
type CssVar = `--rang-${ThemeColor}`;
type Getters<T> = {
[K in keyof T as `get${Capitalize<K & string>}`]: () => T[K];
};
type Dish = { name: string; price: number };
const osh = { name: "Osh", price: 35000 };
const getters: Getters<Dish> = {
getName: () => osh.name,
getPrice: () => osh.price,
};
const variable: CssVar = "--rang-fon";
console.log(variable, getters.getPrice()); // --rang-fon 35000CssVar — uchta satr. Getters<Dish> — { getName: () => string; getPrice: () => number }. Bu — on${…} ishlovchilar bilan bir xil usul: kalitni qayta nomlash va qiymat turini funksiyaga o'rash.
3-mashq (qiyin): Amaliy tajriba — oshxona hodisalari
kurs/mashqlar/15/30-template/kitchen-events.ts faylini yarating:
OrderEvent—"created" | "ready" | "served";EventName—`order:${OrderEvent}`;HandlerName<E>—`on${Capitalize<E>}`;Time—`${number}:${number}`.eventOf(name: string)—"order:ready"→"ready", boshqa satr —null. Ishora: hodisalar ro'yxatias constbilan vafor...of.handlerName(event)—"ready"→"onReady"; natija turiHandlerName<E>.toMinutes(time: Time)—"19:30"→1170. Faqat aniqSS:DDshakli (ikki xonali) va haqiqiy vaqt (soat ≤ 23, daqiqa ≤ 59); aks holdaRangeError. Ishora: RegExp —/^(\d{2}):(\d{2})$/vaexec.
Avval handlerName ni as siz yozib, npx tsc --pretty false ni ishga tushiring. Testlar: eventOf (to'g'ri va begona nom), handlerName (ikki hodisa), toMinutes (to'g'ri vaqtlar; "9:75", "1e1:00", "24:00" — RangeError).
Yechim
as siz variantda — «Qiymatlar dunyosida» bo'limidagi xato, endi faylda:
30-template/kitchen-events.ts(20,3): error TS2322: Type '`on${string}${string}`' is not assignable to type '`on${Capitalize<E>}`'.// 30-template/kitchen-events.ts — «Bahor» oshxona hodisalari
export type OrderEvent = "created" | "ready" | "served";
export type EventName = `order:${OrderEvent}`;
export type HandlerName<E extends string> = `on${Capitalize<E>}`;
export type Time = `${number}:${number}`;
// "order:ready" → "ready"; begona nom — null
export function eventOf(name: string): OrderEvent | null {
for (const event of ["created", "ready", "served"] as const) {
if (name === `order:${event}`) return event;
}
return null;
}
// "ready" → "onReady" (tur ham, qiymat ham)
export function handlerName<E extends string>(
event: E,
): HandlerName<E> {
const first = event.charAt(0).toUpperCase();
return `on${first}${event.slice(1)}` as HandlerName<E>;
}
// "19:30" → 1170; tur faqat shaklni tekshiradi, qolgani — bizda
const TIME_RE = /^(\d{2}):(\d{2})$/;
export function toMinutes(time: Time): number {
const match = TIME_RE.exec(time);
if (match === null) {
throw new RangeError(`Vaqt "SS:DD" bo'lsin: ${time}`);
}
const hour = Number(match[1]);
const minute = Number(match[2]);
if (hour > 23 || minute > 59) {
throw new RangeError(`Bunday vaqt yo'q: ${time}`);
}
return hour * 60 + minute;
}eventOf—as constro'yxat elementieventturi"created" | "ready" | "served", shuning uchunreturn eventOrderEventga mos.handlerNamedagias— darsdagi tuzoq joyi. Uni test himoya qiladi: harfni katta qilish unutilsa, test yiqiladi."1e1:00"— qiziq holat:${number}uni o'tkazadi,Number("1e1")esa10. RegExp bo'lmasa, bu 10:00 bo'lib o'tib ketardi.
// 30-template/kitchen-events.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { eventOf, handlerName, toMinutes } from "./kitchen-events.ts";
test("eventOf: nomdan hodisa", () => {
assert.equal(eventOf("order:ready"), "ready");
assert.equal(eventOf("table:free"), null);
});
test("handlerName: birinchi harf katta", () => {
assert.equal(handlerName("ready"), "onReady");
assert.equal(handlerName("served"), "onServed");
});
test("toMinutes: to'g'ri vaqt", () => {
assert.equal(toMinutes("19:30"), 1170);
assert.equal(toMinutes("07:00"), 420);
});
test("toMinutes: tur o'tkazgan, lekin yo'q vaqt", () => {
assert.throws(() => toMinutes("9:75"), RangeError);
assert.throws(() => toMinutes("1e1:00"), RangeError);
assert.throws(() => toMinutes("24:00"), RangeError);
});npx tsc --pretty false — chiqishsiz. node --test 30-template/kitchen-events.test.ts bizda (Node 24.21.0):
✔ eventOf: nomdan hodisa (1.7145ms)
✔ handlerName: birinchi harf katta (0.3313ms)
✔ toMinutes: to'g'ri vaqt (0.4512ms)
✔ toMinutes: tur o'tkazgan, lekin yo'q vaqt (0.9476ms)
ℹ tests 4
ℹ suites 0
ℹ pass 4
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 259.8732XATOLAR.md ga qo'shing: TS2820 ("Did you mean"), TS2344 (Capitalize<number>), TS2322 ning `on${string}${string}` ko'rinishi. Keyin kurs/mashqlar papkasidan:
git add 15/30-template 15/XATOLAR.md
git commit -m "15/30: kitchen-events.ts — template literal types"11. Real ishda
- Hodisa nomlari va ishlovchilar: DOM'dagi
addEventListenerturlari ("click"→PointerEvent) — xuddiKitchenEventskabi jadval. React'daonClick,onChangeprops nomlari ham shu naqshda (17-qismdan keyin). - Yo'l (route) parametrlari: Express, Next.js kabi freymvorklar (24-qism va keyin)
"/menu/:id"dan{ id: string }turini aynaninfer+ template literal bilan chiqaradi. - Tarjima kalitlari: ko'p tilli saytlarda matnlar
"menu.title","order.ready"kabi kalitlar bilan olinadi. Kalitlar turi tarjima faylidan template literal bilan yasaladi — yo'q kalitni yozsangiz,tscushlaydi. - CSS va dizayn tizimlari:
--rang-${…},`${number}px`kabi naqshlar — dizayn tokenlari turlarida. - Kutubxona muallifi darajasi: murakkab satr tahlili (SQL so'rovini tur darajasida o'qish kabi) — kutubxonalarda uchraydi, lekin kompilyatorni sekinlashtiradi. Ilova kodida oddiy prefiks va
Capitalizeyetadi. - Intervyu: "Template literal type nima?", "
Capitalizeqanday ishlaydi?", "onXishlovchilar turini yasang" — o'rta darajadagi savollar.
Xulosa
`order:${OrderEvent}`— satr turlarini shablondan yasash; union qo'yilsa — hamma kombinatsiyalar (soni ko'paytma bilan o'sadi).Uppercase,Lowercase,Capitalize,Uncapitalize— kompilyatorga qurilgan (intrinsic) harf turlari.- Mapped type +
as+ shablon →onCreated,getPricekabi kalitlar. ${number},${string}— naqshlar: shaklni tekshiradi, ma'noni emas ("0x1F","9:75"o'tadi).infershablon ichida satrni bo'laklaydi. Ishlash paytidagi satrni esa siz yasaysiz —asbilan bergan va'dani test bilan tekshiring.
Keyingi dars: Branded types va variance — bir xil shakldagi, lekin ma'nosi farqli qiymatlarni (TableId va OrderId) adashtirmaslik.
Manbalar
- TypeScript Handbook: "Template Literal Types" (Intrinsic String Manipulation Types) — typescriptlang.org/docs/handbook/2/template-literal-types.html
- TypeScript Release Notes 4.1 (template literal types, key remapping) va 4.5 (template string types as discriminants) — typescriptlang.org/docs/handbook/release-notes/
lib.es5.d.ts—Uppercase,Lowercase,Capitalize,Uncapitalize(intrinsic)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!