IlmHamroh
JavaScript Full-stack/15-qism. TypeScript19/47-dars20 daqiqa
Mundarija (39)

TypeScript enum va uning muqobillari: as const obyekt va literal union

Qisqacha: enum — nomlangan doimiylar to'plami: enum OrderStatus { New, Ready }. U TypeScript'ning kam sonli "kod qo'shadigan" imkoniyatlaridan: tsc undan JavaScript obyekti yasaydi. Shuning uchun Node 24 .ts faylidagi enum ni bajarmaydi, erasableSyntaxOnly esa uni taqiqlaydi. Muqobili — literal union ("yangi" | "tayyor") yoki as const obyekt va undan chiqarilgan union.

Bu darsda

  • Sonli va satrli enum yozasiz, reverse mapping va avtomatik raqamlashni tushunasiz.
  • tsc enum dan qanday JavaScript yasashini ko'rasiz va const enum farqini bilasiz.
  • Sonli enum ning tur tuzog'ini taniysiz.
  • Nega Node type stripping va erasableSyntaxOnly enum ni taqiqlashini tushuntira olasiz.
  • enum ni literal union va as const obyekt bilan almashtirasiz — vazifalar dagi kabi.

Oldin bilishingiz kerak: Klasslar TypeScript'da, Union va literal turlar, Discriminated union va to'liqlik tekshiruvi, Massiv va tuple turlari (as const).

1. Nega bu kerak?

O'tgan darsda parameter properties JavaScript'ga kod qo'shgani uchun Node uni bajarmasligini ko'rdik. enum — xuddi shunday sintaksisning eng mashhur vakili. Uni bilish shart: eski loyihalarda, kutubxonalarda va intervyularda juda ko'p uchraydi. Lekin yangi kodda undan qochish ham kerak.

Avval muammo. «Bahor» oshxonasida buyurtma holatlari raqamlar bilan saqlanardi:

js
// 0 — yangi, 1 — tayyorlanmoqda, 2 — tayyor
const order = { id: 7, status: 1 };
if (order.status === 2) {
  console.log("Olib keting!");
}
order.status = 5;
console.log(order.status); // 5

1 nima? Izohsiz hech kim bilmaydi. 5 — mavjud bo'lmagan holat, lekin JavaScript uni bemalol qabul qildi. "Sehrli raqamlar" (magic numbers) — kod o'qishni qiyinlashtiradigan va jim xatoga yo'l ochadigan narsa. Holatlarga nom berish kerak, va faqat ruxsat etilgan qiymatlar o'tishi kerak.

2. Sonli enum

2.1 Yozuv va qiymatlar

Enum (enumeration — "sanab chiqish") — nomlangan doimiylar to'plami:

ts
enum OrderStatus {
  New,
  Cooking,
  Ready,
}

console.log(OrderStatus.New, OrderStatus.Ready); // 0 2

Qiymat berilmasa, a'zolar 0 dan boshlab raqamlanadi: New — 0, Cooking — 1, Ready — 2. Endi kodda status === 2 o'rniga status === OrderStatus.Ready yoziladi — sehrli raqam nom oldi.

Boshlang'ich qiymatni o'zingiz berishingiz mumkin. Keyingi a'zolar undan davom etadi:

ts
enum Hour {
  Open = 7,
  Lunch = 12,
  AfterLunch,
}

console.log(Hour.AfterLunch); // 13

AfterLunch ga qiymat berilmadi — u oldingisidan bittaga ko'p: 13.

2.2 enum — ham tur, ham qiymat

OrderStatus ikki ma'noda ishlaydi. Tur sifatida — "faqat shu holatlardan biri":

ts
enum OrderStatus {
  New,
  Cooking,
  Ready,
}
const status: OrderStatus = 42;

Kompilyator:

text
TS2322: Type '42' is not assignable to type 'OrderStatus'.

42 — enum a'zolarining birortasi ham emas. Qiymat sifatida esa OrderStatus — haqiqiy JavaScript obyekti, uni konsolga chiqarish, aylanib chiqish mumkin. Buni keyingi bo'limda ko'ramiz.

2.3 Reverse mapping: raqamdan nomga

Sonli enum'da teskari yo'l ham bor — raqam bo'yicha nom olish:

ts
enum OrderStatus {
  New,
  Cooking,
  Ready,
}

console.log(OrderStatus[1]); // Cooking
console.log(Object.keys(OrderStatus));

Konsolda:

text
Cooking
[ '0', '1', '2', 'New', 'Cooking', 'Ready' ]

Bu reverse mapping (teskari moslash) deyiladi. Ikkinchi qatorga qarang: obyektda oltita kalit bor — uchta nom va uchta raqam. Nega? Javob — tsc yasagan JavaScript'da.

3. enum JavaScript'da nimaga aylanadi?

3.1 tsc chiqishi

Oddiy turlar (: number, interface) JavaScript'da shunchaki o'chib ketadi. enum esa tsc 7.0.2 da shunday kodga aylanadi:

js
var OrderStatus;
(function (OrderStatus) {
    OrderStatus[OrderStatus["New"] = 0] = "New";
    OrderStatus[OrderStatus["Cooking"] = 1] = "Cooking";
    OrderStatus[OrderStatus["Ready"] = 2] = "Ready";
})(OrderStatus || (OrderStatus = {}));

Bir qatorni sekin o'qiymiz: OrderStatus[OrderStatus["New"] = 0] = "New". Ichkaridagi OrderStatus["New"] = 0 — obyektga New: 0 yozadi va 0 ni qaytaradi. Tashqaridagisi esa OrderStatus[0] = "New" bo'ladi. Bitta qatorda ikki yo'nalish yoziladi — shuning uchun oltita kalit.

(function (…) { … })(…) — o'zi darhol chaqiriladigan funksiya (IIFE). Bu eski uslub: var va IIFE — ES modullardan oldingi JavaScript'ning naqshi (var va nega undan voz kechildi darsidagi davr). Ya'ni enum — TypeScript'ga ES modullardan ancha oldin, uning ilk versiyalarida qo'shilgan imkoniyat.

Buni rasmda ko'raylik — bitta .ts fayl ikki xil yo'l bilan ishga tushadi:

flowchart LR
  A["status.ts<br/>enum OrderStatus"] --> B["tsc"]
  A --> C["node status.ts<br/>(type stripping)"]
  B --> D["status.js<br/>var + IIFE"]
  C --> E["ERR_UNSUPPORTED_<br/>TYPESCRIPT_SYNTAX"]
  F["type Status =<br/>'yangi' | 'tayyor'"] --> G["node: tur o'chadi"]
  G --> H["ishlaydi"]

Tepada enum — tsc uni JavaScript'ga tarjima qiladi, Node esa tarjima qilmaydi. Pastda union — u faqat tur, o'chiriladi va ikkala yo'lda ham ishlaydi.

3.2 Node 24: "strip-only rejimida qo'llab-quvvatlanmaydi"

TypeScript'ni o'rnatish darsida Node 24 .ts faylni turlarini o'chirib (type stripping) ishga tushirishini ko'rdik. enum ni o'chirib bo'lmaydi — uning o'rniga yuqoridagi IIFE yozilishi kerak. Node buni qilmaydi:

text
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

Tarjimasi: "TypeScript enum i faqat-o'chirish rejimida qo'llab-quvvatlanmaydi". Node'da --experimental-transform-types bayrog'i bor: u enum ni ham o'giradi. Lekin nomidan ko'rinib turibdiki, u tajribaviy (experimental) — ishga tushganda ogohlantirish chiqaradi va xulqi o'zgarishi mumkin.

3.3 erasableSyntaxOnly: xatoni yozish paytida ko'rish

Klasslar TypeScript'da darsidagi bayroq enum ni ham ushlaydi:

ts
// @ts-options: {"erasableSyntaxOnly": true}
enum OrderStatus {
  New,
  Ready,
}

Kompilyator:

text
TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.

Tarjimasi: "erasableSyntaxOnly yoqilganda bu sintaksisga ruxsat yo'q". TypeScript aytyapti: "bu enum ni o'chirib bo'lmaydi, Node uni bajara olmaydi". "O'chiriladigan sintaksis" bayrog'i TypeScript 5.8 da (2025-yil boshi) aynan shu maqsadda qo'shildi: Node, Deno, Bun kabi muhitlar .ts ni turlarini o'chirib bajaradi. Bayroq "faqat shunday muhitda ishlaydigan kod yoz" deydi. U taqiqlaydigan to'rt narsa: enum, qiymatli namespace, parameter properties va import x = require(…) kabi eski import shakli.

Mashqlar papkangizda ham, vazifalar da ham bu bayroq yoqilgan. vazifalar ning AGENTS.md faylidagi qoida: "Faqat o'chirib bo'ladigan TS: enum, namespace, konstruktor parametr xususiyatlari yo'q".

Tekshirib ko'ring: enum Size { S = 10, M, L = 30, XL } — Size.M va Size.XL qiymatlari qanday?

Javob

Size.M — 11 (oldingi 10 dan keyin), Size.XL — 31 (oldingi 30 dan keyin). Qiymat berilmagan sonli a'zo doim oldingi a'zodan bittaga ko'p.

4. Satrli enum

4.1 Har a'zoga satr

Raqamlar loglarda va JSON'da tushunarsiz: "status": 1. Satrli enum'da har a'zo o'z matnini oladi:

ts
enum Payment {
  Cash = "naqd",
  Card = "karta",
}

console.log(Payment.Card);
console.log(Object.keys(Payment), Object.values(Payment));

Konsolda:

text
karta
[ 'Cash', 'Card' ] [ 'naqd', 'karta' ]

Satrli enum'da reverse mapping yo'q: tsc faqat Payment["Cash"] = "naqd" yozadi. Kalitlar — faqat nomlar.

4.2 Satrli enum satrni qabul qilmaydi

Mana kutilmagan joy:

ts
enum Payment {
  Cash = "naqd",
  Card = "karta",
}
function pay(method: Payment): string {
  return `To'lov: ${method}`;
}
pay("karta");

Kompilyator:

text
TS2345: Argument of type '"karta"' is not assignable to parameter of type 'Payment'.

Tarjimasi: ""karta" turidagi argumentni Payment turidagi parametrga berib bo'lmaydi". Qiymat bir xil — "karta", lekin TypeScript qabul qilmaydi: faqat Payment.Card deb yozish kerak. Satrli enum — TypeScript'dagi kam sonli nominal turlardan biri: unda nom muhim, shakl emas (Strukturaviy tiplash darsidagi qoidaning istisnosi).

Bu ba'zan foydali (tasodifiy satr o'tib ketmaydi), lekin ko'pincha noqulay. Serverdan JSON keladi: {"method": "karta"}. Bu oddiy satr, enum emas — har safar o'girish kerak bo'ladi.

5. Sonli enum tuzog'i: istalgan son o'tadi

42 literalini OrderStatus ga berish xato edi. Lekin number turidagi o'zgaruvchi-chi?

ts
enum OrderStatus {
  New,
  Cooking,
  Ready,
}

const fromServer: number = 42;
const status: OrderStatus = fromServer;
console.log(status, OrderStatus[status]); // 42 undefined

tsc — xatosiz. number turidagi har qanday qiymat sonli enum'ga beriladi. Natija: status turi OrderStatus, qiymati esa mavjud bo'lmagan 42. Turi bor, ma'nosi yo'q.

Nega TypeScript bunga ruxsat beradi? Sonli enum'ning bir eski qo'llanishi — bit bayroqlari (bit flags): har a'zo ikkining darajasi, ular | (bitli "yoki") bilan birlashtiriladi:

ts
enum Access {
  Read = 1,
  Write = 2,
  Delete = 4,
}

const cashier: Access = Access.Read | Access.Write;
console.log(cashier); // 3
console.log((cashier & Access.Write) !== 0); // true
console.log((cashier & Access.Delete) !== 0); // false

Kassirga o'qish va yozish ruxsati berildi: 1 + 2 = 3. 3 esa enum'da nomli a'zo emas — lekin to'g'ri qiymat. & (bitli "va") bilan har ruxsat alohida tekshiriladi. Shunday qiymatlar o'tishi uchun TypeScript sonli enum'ga istalgan number ni qabul qiladi — va shu bilan tasodifiy 42 ham o'tib ketadi. Bit amallari kundalik veb-ishda kam uchraydi, ularni hozir chuqur o'rganish shart emas.

Satrli enum'da bunday teshik yo'q. Literal union'da ham yo'q: number ni 0 | 1 | 2 ga berib bo'lmaydi.

6. const enum

6.1 Obyektsiz enum

const enum — tsc obyekt yaratmaydigan enum. Har ishlatilgan joyga qiymatning o'zi qo'yiladi:

ts
const enum Size {
  S,
  M,
  L,
}
console.log(Size.M); // 1

tsc chiqishi — bitta qator, Size obyekti umuman yo'q:

js
console.log(1 /* Size.M */);

Kichikroq va tezroq ko'rinadi. Lekin bu yozuvning o'z muammolari bor. Birinchisi: Size[1] (reverse mapping) ishlamaydi. Ikkinchisi jiddiyroq: const enum boshqa fayldan import qilinsa, har faylni alohida o'giradigan vositalar (Babel, esbuild, Node type stripping) uning qiymatini bila olmaydi — ular boshqa faylni o'qimaydi. TypeScript hujjatlari kutubxonalarda const enum ni eksport qilmaslikni tavsiya qiladi. erasableSyntaxOnly ham uni taqiqlaydi (TS1294).

6.2 enum turlari — xulosa

Sonli enum Satrli enum const enum
JavaScript'da obyekt (IIFE) obyekt (IIFE) yo'q, qiymat qo'yiladi
Reverse mapping bor yo'q yo'q
Istalgan number o'tadimi ha (tuzoq) — ha
Node type stripping yo'q yo'q yo'q

Tekshirib ko'ring: enum Level { Low = "past", High = "baland" } — Object.keys(Level).length nechaga teng? Sonli enum Level { Low, High } da-chi?

Javob

Satrli enum'da — 2 (Low, High): reverse mapping yo'q. Sonli enum'da — 4 ("0", "1", Low, High): har a'zo ikki yo'nalishda yoziladi. Shuning uchun sonli enum'ni Object.keys bilan aylanish xato beradi — raqamlar ham aralashib keladi.

7. Muqobil 1: literal union

7.1 Eng sodda yo'l

Union va literal turlar darsida bu yo'lni bilasiz:

ts
type OrderStatus = "yangi" | "tayyorlanmoqda" | "tayyor";

function canServe(status: OrderStatus): boolean {
  return status === "tayyor";
}

console.log(canServe("tayyor")); // true

Enum bilan solishtiring:

  • JavaScript'da hech narsa qolmaydi — tur o'chadi, "tayyor" oddiy satr. Node type stripping, erasableSyntaxOnly — hammasi rozi.
  • JSON bilan tabiiy ishlaydi — serverdan kelgan "tayyor" aynan shu qiymat, o'girish kerak emas.
  • Noto'g'ri qiymat — darhol xato: canServe("Tayyor") — TS2345. Muharrir avtomatik to'ldirishda uchta variantni taklif qiladi.
  • Sonli enum teshigi yo'q: istalgan satr OrderStatus ga o'tmaydi.

vazifalar da hamma "enum'simon" turlar shunday yozilgan (turlar.ts):

ts
export type Filtr = "hammasi" | "faol" | "bajarilgan";
export type Saralash = "standart" | "yangi" | "alifbo";
export type TarmoqHolati =
  "yuklanmoqda" | "tayyor" | "xato" | "oflayn";

7.2 To'liqlik tekshiruvi union bilan

Discriminated union darsidagi never tekshiruvi union bilan ham ishlaydi. Yangi holat qo'shilsa, switch uni unutganini tsc aytadi:

ts
type OrderStatus = "yangi" | "tayyor" | "bekor";

function isFinal(status: OrderStatus): boolean {
  switch (status) {
    case "yangi":
      return false;
    case "tayyor":
      return true;
    default: {
      const rest: never = status;
      return rest;
    }
  }
}

Kompilyator:

text
TS2322: Type '"bekor"' is not assignable to type 'never'.

""bekor" ni never ga berib bo'lmaydi" — ya'ni switch da "bekor" uchun case yo'q. Xabar unutilgan qiymatning nomini ham aytadi.

Tekshirib ko'ring: type Pay = "naqd" | "karta" va enum Pay2 { Cash = "naqd", Card = "karta" }. Serverdan {"method": "karta"} keldi va JSON.parse qildingiz. Qaysi turga uni o'girishsiz berish oson?

Javob

Pay ga. Union — oddiy satrlar to'plami, "karta" unga to'g'ridan-to'g'ri mos (avval qiymat ruxsat etilganini tekshirish kerak, albatta). Pay2 esa satrni qabul qilmaydi — Pay2.Card kerak. Tekshirilgan satrni enum'ga aylantirish uchun qo'shimcha moslash kodi yoziladi.

8. Muqobil 2: as const obyekt

8.1 Qachon obyekt kerak?

Literal union faqat tur. Ba'zan esa holatlar ishlash paytida ham kerak: ro'yxatini chiqarish, OrderStatus.Ready kabi nom bilan murojaat qilish (masalan, enum'li eski kodni ko'chirayotganda). Buning uchun Massiv va tuple turlari darsidagi as const obyektga qo'llanadi:

ts
const ORDER_STATUS = {
  New: "yangi",
  Cooking: "tayyorlanmoqda",
  Ready: "tayyor",
} as const;

console.log(ORDER_STATUS.Ready);
console.log(Object.values(ORDER_STATUS));

Konsolda:

text
tayyor
[ 'yangi', 'tayyorlanmoqda', 'tayyor' ]

as const siz hover'da { New: string; Cooking: string; Ready: string } bo'lardi — qiymatlar string ga kengayardi. as const bilan esa { readonly New: "yangi"; readonly Cooking: "tayyorlanmoqda"; readonly Ready: "tayyor" } — har qiymat literal tur va hammasi readonly. Bu — oddiy JavaScript obyekti, o'chiriladigan hech narsa yo'q, faqat as const turi o'chadi.

8.2 Obyektdan union chiqarish

Endi obyektdagi qiymatlardan union turini avtomatik yasaymiz, ikkinchi marta yozmasdan:

ts
const ORDER_STATUS = {
  New: "yangi",
  Cooking: "tayyorlanmoqda",
  Ready: "tayyor",
} as const;

type OrderStatus = (typeof ORDER_STATUS)[keyof typeof ORDER_STATUS];

function label(status: OrderStatus): string {
  return status === ORDER_STATUS.Ready ? "Olib keting!" : "Kuting";
}

console.log(label("tayyor")); // Olib keting!
console.log(label(ORDER_STATUS.New)); // Kuting

OrderStatus turi — "yangi" | "tayyorlanmoqda" | "tayyor". Uzun ifodani qismlarga ajratib o'qiymiz:

Qism Ma'nosi Natija
typeof ORDER_STATUS obyektning turi { readonly New: "yangi"; … }
keyof typeof ORDER_STATUS uning kalitlari "New" | "Cooking" | "Ready"
(…)[keyof …] shu kalitlardagi qiymatlar "yangi" | "tayyorlanmoqda" | "tayyor"

Bu uch operatorni (keyof, typeof va indexed access) keyingi darslarda alohida o'rganamiz. Hozircha bu qatorni tayyor qolip deb bilish yetarli: "obyekt qiymatlarining union'i".

Qiymat va tur bir xil nom (OrderStatus) bilan ham yozilishi mumkin — TypeScript qiymatlar va turlarni alohida saqlaydi. Ko'p loyihalar shunday qiladi: OrderStatus.Ready (qiymat) va status: OrderStatus (tur) — xuddi enum'dagidek o'qiladi.

Endi label ham ORDER_STATUS.New ni, ham oddiy "tayyor" satrini qabul qiladi. Satrli enum'dagi noqulaylik yo'q.

8.3 Har holatga matn: satisfies Record

Holatlar uchun ekrandagi matnlar — Index signature va Record darsidagi Record<Union, V> ning tabiiy joyi. satisfies bilan tekshiramiz:

ts
type OrderStatus = "yangi" | "tayyor" | "bekor";

const LABELS = {
  yangi: "Qabul qilindi",
  tayyor: "Tayyor",
} satisfies Record<OrderStatus, string>;

Kompilyator:

text
TS2741: Property 'bekor' is missing in type '{ yangi: string; tayyor: string; }' but required in type 'Record<OrderStatus, string>'.

Tarjimasi: "bekor xususiyati { yangi: string; tayyor: string; } turida yo'q, lekin Record<OrderStatus, string> da majburiy". Holat qo'shildi — matn unutildi — tsc darhol eslatdi. vazifalar dagi aloqaMatnlari (satisfies Record<TarmoqHolati, string>) aynan shunday.

8.4 Massivdan union: ro'yxat ham, tur ham

Holatlarga nom (ORDER_STATUS.Ready) kerak bo'lmasa, faqat ro'yxat yetarli bo'lsa, as const massiv yanada sodda:

ts
const SIZES = ["kichik", "o'rta", "katta"] as const;
type Size = (typeof SIZES)[number];

function isSize(text: string): text is Size {
  return SIZES.some((s) => s === text);
}

console.log(isSize("o'rta"), isSize("XL")); // true false

(typeof SIZES)[number] — "massivning istalgan raqamli indeksidagi element turi", ya'ni "kichik" | "o'rta" | "katta". Ro'yxat bir marta yoziladi: undan ham tugmalar chiziladi, ham tur chiqadi. vazifalar dagi FILTRLAR ham ro'yxat, lekin teskari yo'l bilan: avval Filtr turi, keyin unga mos readonly Filtr[] massiv. Ikkala yo'l ham ishlaydi — muhimi, biri o'zgarsa tsc ikkinchisini eslatsin.

Tekshirib ko'ring: const SIZE = { S: "kichik", L: "katta" }; (as const siz) dan (typeof SIZE)[keyof typeof SIZE] qanday tur beradi?

Javob

string. as const bo'lmasa, obyekt qiymatlari string ga kengayadi, qiymatlar union'i ham string bo'ladi — literal ro'yxat yo'qoladi. as const — bu usulning yuragi.

9. Qachon enum ni ko'rasiz?

enum noto'g'ri emas — u ishlaydi va tsc bilan build qilinadigan loyihalarda (Angular, ko'p korporativ kodlar) keng tarqalgan. Siz uni o'qiy olishingiz va o'zgartira olishingiz shart. Yangi kodda tanlov mezonlari:

Holat Tanlov
Faqat tur kerak (parametr, maydon) literal union
Qiymatlar ro'yxati ishlash paytida ham kerak as const obyekt + union
erasableSyntaxOnly yoki Node type stripping faqat yuqoridagi ikkitasi
Eski loyihada enum bor uslubni saqlang, ko'chirish — alohida qaror

10. Ko'p uchraydigan xatolar

10.1 Node'da enum

ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX: TypeScript enum is not supported in strip-only mode. Tuzatish: literal union yoki as const obyekt. erasableSyntaxOnly ni yoqing — xato tsc da ko'rinadi (TS1294).

10.2 Sonli enum'ni Object.keys bilan aylanish

Nomlar bilan birga raqamlar ham keladi. Tuzatish: as const obyekt va Object.values.

10.3 Satrli enum'ga satr berish

TS2345: Argument of type '"karta"' is not assignable to parameter of type 'Payment'. Tuzatish: Payment.Card yoki — yaxshisi — union turi.

10.4 as const ni unutish

(typeof X)[keyof typeof X] — string. Tuzatish: obyekt oxirida as const.

10.5 Serverdan kelgan satrga ko'r-ko'rona ishonish

const s = json.status as OrderStatus — tekshiruvsiz va'da. Tuzatish: ruxsat etilgan qiymatlar ro'yxatidan find bilan qidiring (3-mashqdagi parseStatus).

11. Mashqlar

1-mashq (oson): Qiymatlarni ayting

ts
enum Table {
  Window = 1,
  Corner,
  Terrace = 10,
  Hall,
}
console.log(Table.Corner + Table.Hall); // 13

Table.Corner qiymati: [:2]. Table.Hall qiymati: [:11]. Object.keys(Table).length: .

Yechim
  • Corner — 2: Window = 1 dan keyingisi.
  • Hall — 11: Terrace = 10 dan keyingisi. 2 + 11 = 13.
  • 8: sonli enum'da har a'zo ikki marta — nom va raqam ("1", "2", "10", "11" va to'rtta nom).

2-mashq (o'rta): enum ni union'ga ko'chiring

Bu kod tsc bilan build qilinganda ishlaydi, lekin Node 24 da ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX beradi. Yana bir kamchiligi: yangi yetkazish turi qo'shilsa, if uni jim "kuryer" narxiga tushirib yuboradi:

ts
enum Delivery {
  Pickup = "olib ketish",
  Courier = "kuryer",
}
function deliveryFee(type: Delivery, km: number): number {
  if (type === Delivery.Pickup) return 0;
  return 10000 + km * 2000;
}

Uni enum'siz yozing: as const obyekt DELIVERY, undan chiqarilgan type Delivery, va deliveryFee — switch + never bilan (yangi yetkazish turi qo'shilsa, tsc aytsin).

Yechim
ts
// @ts-options: {"erasableSyntaxOnly": true}
const DELIVERY = {
  Pickup: "olib ketish",
  Courier: "kuryer",
} as const;

type Delivery = (typeof DELIVERY)[keyof typeof DELIVERY];

function deliveryFee(type: Delivery, km: number): number {
  switch (type) {
    case "olib ketish":
      return 0;
    case "kuryer":
      return 10000 + km * 2000;
    default: {
      const rest: never = type;
      return rest;
    }
  }
}

console.log(deliveryFee(DELIVERY.Courier, 3)); // 16000
console.log(deliveryFee("olib ketish", 3)); // 0
  • erasableSyntaxOnly yoqilgan — kod o'chiriladigan sintaksisda, Node uni bajaradi.
  • DELIVERY.Courier ham, oddiy "olib ketish" ham qabul qilinadi.
  • DELIVERY ga Drone: "dron" qo'shsangiz, default dagi qator TS2322: Type '"dron"' is not assignable to type 'never'. beradi.

3-mashq (qiyin): Amaliy tajriba — buyurtma holatlari

Mashq kurs/mashqlar/15/ papkasida (erasableSyntaxOnly yoqilgan). 19-holatlar/status.ts ni avval enum bilan yozing:

ts
// 19-holatlar/status.ts — birinchi variant (enum bilan)
export enum OrderStatus {
  New = "yangi",
  Cooking = "tayyorlanmoqda",
  Ready = "tayyor",
  Served = "berildi",
}

export function nextStatus(s: OrderStatus): OrderStatus {
  switch (s) {
    case OrderStatus.New:
      return OrderStatus.Cooking;
    case OrderStatus.Cooking:
      return OrderStatus.Ready;
    default:
      return OrderStatus.Served;
  }
}

npx tsc --pretty false va node --test 19-holatlar/status.test.ts (bitta oddiy test bilan) nima deydi? Keyin enum'siz qayta yozing:

  • ORDER_STATUS — as const obyekt, OrderStatus — undan union.
  • STATUS_LABELS — har holatga o'zbekcha matn, satisfies Record<OrderStatus, string>.
  • nextStatus(s) — switch + never (default siz qolgan holat yo'q).
  • parseStatus(text: string): OrderStatus — serverdan kelgan matn; noma'lum bo'lsa TypeError. Ishora: Object.values(ORDER_STATUS).find((s) => s === text) — find qiymatni ham, turni ham toraytiradi (vazifalar dagi FILTRLAR.find kabi).

Testlar: holatlar ketma-ketligi, parseStatus (to'g'ri, katta harfli "Tayyor", "1"), matnlar.

Yechim

Birinchi variantda tsc 7.0.2:

text
19-holatlar/status.ts(2,13): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.

node --test esa testgacha yetmadi — modul yuklanishida to'xtadi:

text
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode
✖ 19-holatlar\status.test.ts (194.4799ms)

Ikkalasi bitta sababni aytadi: enum o'chirib bo'lmaydigan sintaksis. tsc buni yozish paytida, Node — ishga tushirish paytida ko'rsatdi.

ts
// 19-holatlar/status.ts — buyurtma holatlari (enum'siz)
export const ORDER_STATUS = {
  New: "yangi",
  Cooking: "tayyorlanmoqda",
  Ready: "tayyor",
  Served: "berildi",
} as const;

export type OrderStatus =
  (typeof ORDER_STATUS)[keyof typeof ORDER_STATUS];

// Har holatga ekrandagi matn — biri unutilsa, tsc aytadi
export const STATUS_LABELS = {
  yangi: "Qabul qilindi",
  tayyorlanmoqda: "Tayyorlanmoqda",
  tayyor: "Tayyor",
  berildi: "Mehmonga berildi",
} satisfies Record<OrderStatus, string>;

export function nextStatus(s: OrderStatus): OrderStatus {
  switch (s) {
    case "yangi":
      return "tayyorlanmoqda";
    case "tayyorlanmoqda":
      return "tayyor";
    case "tayyor":
    case "berildi":
      return "berildi";
    default: {
      const rest: never = s;
      return rest;
    }
  }
}

// Serverdan kelgan matn → holat (yoki xato)
export function parseStatus(text: string): OrderStatus {
  const found = Object.values(ORDER_STATUS).find((s) => s === text);
  if (found === undefined) {
    throw new TypeError(`Noma'lum holat: ${text}`);
  }
  return found;
}
ts
// 19-holatlar/status.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import {
  ORDER_STATUS,
  STATUS_LABELS,
  nextStatus,
  parseStatus,
} from "./status.ts";

test("nextStatus: holatlar ketma-ketligi", () => {
  assert.equal(nextStatus(ORDER_STATUS.New), "tayyorlanmoqda");
  assert.equal(nextStatus("tayyorlanmoqda"), "tayyor");
  assert.equal(nextStatus("berildi"), "berildi");
});

test("parseStatus: serverdan kelgan matn", () => {
  assert.equal(parseStatus("tayyor"), ORDER_STATUS.Ready);
  assert.throws(() => parseStatus("Tayyor"), TypeError);
  assert.throws(() => parseStatus("1"), TypeError);
});

test("STATUS_LABELS: har holatga matn", () => {
  const all = Object.values(ORDER_STATUS);
  assert.equal(all.length, 4);
  assert.equal(STATUS_LABELS[parseStatus("tayyor")], "Tayyor");
});
  • Object.values(ORDER_STATUS) — ("yangi" | "tayyorlanmoqda" | "tayyor" | "berildi")[], sonli enum'dagi raqamlar aralashmaydi. find natijasi — OrderStatus | undefined, tekshiruvdan keyin OrderStatus.
  • case "tayyor": case "berildi": — ikki case bitta javobga tushadi (oxirgi holat o'zida qoladi).
  • parseStatus("Tayyor") — xato: holatlar katta-kichik harfga sezgir, server aynan "tayyor" yuborishi kerak.

npx tsc --pretty false — chiqishsiz. node --test 19-holatlar/status.test.ts bizda (Node 24.21.0) shunday chiqdi:

text
✔ nextStatus: holatlar ketma-ketligi (3.7521ms)
✔ parseStatus: serverdan kelgan matn (1.6521ms)
✔ STATUS_LABELS: har holatga matn (0.485ms)
ℹ tests 3
ℹ suites 0
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 365.1981

XATOLAR.md ga qo'shing: TS1294 (enum), Node'ning ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, satrli enum'ga satr (TS2345), satisfies Record dagi TS2741. Keyin kurs/mashqlar papkasidan:

bash
git add 15/19-holatlar 15/XATOLAR.md
git commit -m "15/19: status.ts — enum o'rniga as const va union"

12. Real ishda

  • Eski kod va kutubxonalar: Angular, ko'p korporativ loyihalar va avtomatik yaratiladigan API mijozlarida enum ko'p. Uni o'qish, IIFE chiqishini tanish va reverse mapping'ni bilish — majburiy ko'nikma.
  • Zamonaviy yo'nalish: Node, Deno, Bun .ts ni type stripping bilan bajaradi, ko'p jamoalar erasableSyntaxOnly ni yoqadi. Shuning uchun yangi kodda union va as const obyekt tobora ko'proq uchraydi.
  • API va JSON: literal union serverdan kelgan satr bilan to'g'ridan-to'g'ri mos. Uni ishlash paytida tekshirish (find, keyinroq Zod) — API ma'lumotlarini tiplash darsida.
  • Intervyu: "enum va union farqi?", "Reverse mapping nima?", "const enum qachon xavfli?", "Nega Node enum ni bajarmaydi?" — tez-tez so'raladi.

Xulosa

  • enum — nomlangan doimiylar: sonli (0 dan, reverse mapping bilan) va satrli (reverse mapping'siz, satrni qabul qilmaydi).
  • enum JavaScript'ga kod qo'shadi (var + IIFE). Node type stripping uni bajarmaydi, erasableSyntaxOnly esa TS1294 beradi.
  • Sonli enum'ga istalgan number o'tadi — tur tuzog'i. const enum — obyektsiz, lekin alohida o'giriladigan vositalar bilan xavfli.
  • Muqobil: faqat tur kerak bo'lsa — literal union; qiymatlar ro'yxati ham kerak bo'lsa — as const obyekt va (typeof X)[keyof typeof X].
  • switch + never va satisfies Record<Union, V> — yangi holat qo'shilganda unutilgan joylarni tsc ko'rsatadi.

Keyingi dars: Generics: turni parametr qilish — bitta funksiya turli turlar bilan ishlaydi va tur ma'lumotini yo'qotmaydi: first<T>(items: T[]): T.

Manbalar

  • TypeScript Handbook: "Enums" (Numeric, String, Reverse mappings, const enums, Objects vs Enums) — typescriptlang.org/docs/handbook/enums.html
  • TypeScript 5.8 Release Notes: "The --erasableSyntaxOnly Option" — typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html
  • Node.js hujjatlari: "Modules: TypeScript" (Type stripping, unsupported features) — nodejs.org/api/typescript.html
  • TypeScript Handbook: "Everyday Types" (Literal Types) — typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
TypeScript enum va uning muqobillari: as const obyekt va literal union — IlmHamroh