IlmHamroh
JavaScript Full-stack/15-qism. TypeScript18/47-dars23 daqiqa
Mundarija (38)

TypeScript klasslari: private, readonly, abstract va implements

Qisqacha: TypeScript klassida har maydon e'lon qilinadi (id: number;) va unga kirish modifikatori beriladi: public (sukut bo'yicha), private, protected. Ular faqat tsc tekshiruvida yashaydi — ishlash paytida haqiqiy yopiqlik # da. readonly maydonni faqat konstruktorda yozishga ruxsat beradi, abstract klass — "chala qolip", implements esa klass interfeysga mosligini tekshiradi.

Bu darsda

  • Klass maydonlarini turlari bilan e'lon qilasiz va strictPropertyInitialization xabarini tushunasiz.
  • private/protected va # farqini ayta olasiz: biri tekshiruvda, ikkinchisi ishlash paytida.
  • readonly, static readonly va parameter properties yozasiz — va nega vazifalar da parameter properties yo'qligini bilasiz.
  • abstract klass, implements va override bilan klasslar ierarxiyasini qurasiz.
  • vazifalar dagi uchta klass modulini .ts ga ko'chirasiz.

Oldin bilishingiz kerak: Index signature va Record, Meros: extends va super, Private # maydon va metodlar, Getter/setter va static a'zolar.

1. Nega bu kerak?

O'tgan darsda obyektlarni turlar bilan yozdik. Klasslar ham obyekt yasaydi, lekin JavaScript'da ularning maydonlari hech qayerda e'lon qilinmaydi — konstruktorda this.x = … deyilgan joyda paydo bo'ladi.

Sardor buyurtma klassini yozdi:

js
class Order {
  constructor(id) {
    this.id = id;
    this.items = [];
  }
  add(dish) {
    this.itmes.push(dish);
  }
}

const order = new Order(1);
order.add("Osh");

Konsolda:

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

itmes — harf almashgan. JavaScript uchun bu boshqa, mavjud bo'lmagan xususiyat, demak undefined. Xato faqat add chaqirilganda chiqadi — balki bir hafta keyin, mehmon buyurtma berayotganda.

Ikkinchi muammo: klassning ichki holati tashqaridan buziladi. Kimdir order.items = null yozib qo'ysa, klass bunga to'sqinlik qilmaydi. TypeScript bu ikkalasini ham hal qiladi: klass maydonlari ro'yxati e'lon qilinadi va kim nimaga tegishi mumkinligi yoziladi.

2. Maydonlarni e'lon qilish

2.1 Har maydon — e'lon bilan

«Nega bu kerak?» bo'limidagi klassni so'zma-so'z .ts ga ko'chiramiz:

ts
class Order {
  constructor(id: number) {
    this.id = id;
  }
}

Kompilyator:

text
TS2339: Property 'id' does not exist on type 'Order'.

Tarjimasi: "Order turida id xususiyati yo'q". JavaScript'da this.id = id maydonni yaratardi. TypeScript'da esa klass turi — e'lon qilingan maydonlar ro'yxati. id u yerda yo'q, demak unga yozib ham bo'lmaydi.

Tuzatish — klass tanasida maydonni turi bilan e'lon qilish. Bu Private # maydon darsidagi klass maydonlari (class fields) yozuvi, faqat endi tur bilan:

ts
class Order {
  id: number;
  items: string[] = [];

  constructor(id: number) {
    this.id = id;
  }

  add(dish: string): void {
    this.items.push(dish);
  }
}

const order = new Order(1);
order.add("Osh");
console.log(order.items); // [ 'Osh' ]

items ga boshlang'ich qiymat berildi — tur undan ham chiqarilishi mumkin edi, lekin bo'sh massivga tur yozamiz (Massiv va tuple turlari darsidagi qoida). Endi this.itmes yozib ko'ring:

ts
class Order {
  items: string[] = [];
  add(dish: string): void {
    this.itmes.push(dish);
  }
}

Kompilyator:

text
TS2551: Property 'itmes' does not exist on type 'Order'. Did you mean 'items'?

"Shunday xususiyat yo'q. items demoqchimidingiz?" — TypeScript yaqin nomni ham taklif qildi. Bir hafta keyin chiqadigan TypeError yozish paytiga ko'chdi.

2.2 Maydon albatta qiymat olsin

strict rejimda yana bir tekshiruv bor — strictPropertyInitialization ("xususiyatlarni qat'iy boshlash"). E'lon qilingan maydon konstruktor tugaguncha qiymat olishi kerak:

ts
class Order {
  id: number;
  total: number;
  constructor(id: number) {
    this.id = id;
  }
}

Kompilyator:

text
TS2564: Property 'total' has no initializer and is not definitely assigned in the constructor.

Tarjimasi: "total xususiyatida boshlang'ich qiymat yo'q va u konstruktorda aniq berilmagan". Aks holda order.total turi number, qiymati esa undefined bo'lardi — null va undefined darsidagi tuzoq. Uch tuzatish bor: boshlang'ich qiymat (total = 0), konstruktorda berish, yoki turni rost yozish (total: number | undefined).

To'rtinchi yo'l ham uchraydi — total!: number. Undov belgisi "men bilaman, u albatta beriladi" degan va'da, non-null assertion kabi. TypeScript bu va'dani tekshirmaydi, shuning uchun uni faqat qiymat boshqa joyda (masalan, kutubxona tomonidan) beriladigan holatlar uchun qoldiring.

2.3 JSDoc .ts da ishlamaydi

vazifalar dagi vazifa.js 12-qismdan beri // @ts-check va JSDoc bilan tekshirilardi. U .ts ga faqat nomi o'zgartirilganda, tsc 53 ta xato topdi. Ulardan biri — VazifaXatosi klassida:

text
assets/js/vazifa.ts(14,10): error TS2339: Property 'kod' does not exist on type 'VazifaXatosi'.

JS faylda /** @type {VazifaXatosiKodi} */ this.kod = … maydonni e'lon qilardi. .ts faylda esa JSDoc'dagi turlar hisobga olinmaydi — tur faqat TypeScript sintaksisidan olinadi. Xuddi shu sababli royxat.ts dagi /** @type {Vazifa[]} */ #vazifalar = []; turini yo'qotdi va never[] ga aylandi. Keyin uning har ishlatilishi Property 'id' does not exist on type 'never' berdi (11 ta). Bu xatolarni «Vazifalar qadami» mashqida tuzatamiz.

Tekshirib ko'ring: class Guest { name: string; constructor() {} } — qaysi xato chiqadi va eng sodda tuzatish qanday?

Javob

TS2564: Property 'name' has no initializer… — name e'lon qilingan, lekin hech qayerda qiymat olmaydi. Eng sodda tuzatish — konstruktorga parametr qo'shib, this.name = name yozish. Yoki mehmon nomi bo'lmasligi mumkin bo'lsa — name: string | undefined (yoki ixtiyoriy name?: string).

3. Kirish modifikatorlari: public, private, protected

3.1 Uchta daraja

TypeScript har maydon va metod oldiga kirish modifikatori (access modifier) qo'yishga ruxsat beradi — "kim tega oladi":

Modifikator Klass ichida Bola klassda Tashqarida
public (sukut bo'yicha) ha ha ha
protected ha ha yo'q
private ha yo'q yo'q
ts
class Table {
  private guests = 0;
  protected seats = 4;
}
class VipTable extends Table {
  describe(): string {
    return `${this.seats} o'rin, ${this.guests} mehmon`;
  }
}
const table = new Table();
table.guests = 2;
table.seats = 6;

Kompilyator:

text
TS2341: Property 'guests' is private and only accessible within class 'Table'.
TS2341: Property 'guests' is private and only accessible within class 'Table'.
TS2445: Property 'seats' is protected and only accessible within class 'Table' and its subclasses.
  • TS2341: "guests — private, faqat Table klassi ichida ochiq". Ikki marta: bola klassdagi this.guests va tashqaridagi table.guests.
  • TS2445: "seats — protected, faqat Table va uning bola klasslari ichida ochiq". VipTable ichida this.seats mumkin, tashqarida — yo'q.

public ni odatda yozishmaydi — modifikatorsiz a'zo shundoq ham ochiq.

3.2 private va # — farqi nimada?

Private # maydon darsida JavaScript'ning o'z yopiq maydonlarini o'rgangansiz. Ikkalasi o'xshash ko'rinadi, lekin ular butunlay boshqa narsalar:

ts
class Account {
  private balance = 0;
  #pin = "0000";
  deposit(sum: number): void {
    this.balance += sum;
  }
  checkPin(pin: string): boolean {
    return pin === this.#pin;
  }
}

const account = new Account();
account.deposit(50000);
console.log(account["balance"]); // 50000
console.log(JSON.stringify(account)); // {"balance":50000}
console.log(account.checkPin("1234")); // false

tsc xatosiz o'tkazdi, lekin natijaga qarang:

  • account["balance"] — kvadrat qavs bilan private maydonni o'qish mumkin. TypeScript buni ataylab teshik sifatida qoldirgan (testlarda ichki holatni ko'rish uchun).
  • JSON.stringify balance ni chiqardi. tsc chiqaradigan JavaScript'da private so'zi yo'qoladi va maydon oddiy, ochiq maydon bo'lib qoladi: balance = 0;.
  • #pin esa na JSON.stringify da, na boshqa joyda ko'rinmaydi. account.#pin deb yozish — sintaksis xatosi, tashqi kod uni hatto o'qiy olmaydi.

Tashqaridan # ga murojaat qilsangiz, tsc ham to'xtatadi — bu xabar boshqacha:

ts
class Account {
  #pin = "0000";
}
const account = new Account();
console.log(account.#pin);

Kompilyator:

text
TS18013: Property '#pin' is not accessible outside class 'Account' because it has a private identifier.

Tarjimasi: "#pin xususiyati Account klassidan tashqarida ochiq emas, chunki u yopiq identifikatorga ega".

private x #x
Kim tekshiradi faqat tsc JavaScript dvigateli
Ishlash paytida oddiy ochiq maydon haqiqatan yopiq
obj["x"] bilan o'qiladi imkonsiz
JSON.stringify da chiqadi chiqmaydi

Qoida: haqiqiy yopiqlik kerak bo'lsa (parol, ichki holat) — #. Loyihamizda ham shunday: Vazifa ning #matn maydoni 10-qismdan beri #. Eski kodda va kutubxonalarda esa private ko'p uchraydi — u # paydo bo'lishidan (ES2022) ancha oldin bor edi. Yana bir farq: protected ning # da muqobili yo'q. Bola klassga ochiq, tashqariga yopiq a'zo kerak bo'lsa, protected ishlatiladi.

Tekshirib ko'ring: Kutubxona class Cart { private items: string[] = [] } eksport qiladi. Siz uni JavaScript fayldan ishlatyapsiz. cart.items.push("osh") ishlaydimi?

Javob

Ishlaydi. JavaScript faylda tsc tekshiruvi yo'q, private esa build'da o'chib ketgan — items oddiy ochiq maydon. #items bo'lganda — SyntaxError: tashqaridan # ga murojaat JavaScript'ning o'zida taqiqlangan.

4. readonly: faqat konstruktorda yoziladi

4.1 O'zgarmas maydon

Obyekt turlari va massivlardagi readonly klass maydonlarida ham bor. Bunday maydonga faqat e'londa yoki konstruktorda qiymat beriladi:

ts
class Dish {
  static readonly MAX_NAME = 40;
  readonly id: number;
  constructor(id: number) {
    this.id = id;
  }
  rename(): void {
    this.id = 7;
  }
}
Dish.MAX_NAME = 100;

Kompilyator:

text
TS2540: Cannot assign to 'id' because it is a read-only property.
TS2540: Cannot assign to 'MAX_NAME' because it is a read-only property.

Tarjimasi: "id ga yozib bo'lmaydi, chunki u faqat o'qish uchun". Metod ichida ham — readonly maydon obyekt yaratilgandan keyin o'zgarmaydi. static readonly — klassning o'ziga tegishli doimiy (static a'zolar): vazifalar dagi Vazifa.MAKS_UZUNLIK = 100 aynan shunday.

4.2 Tuzoq: readonly sayoz

readonly faqat maydonni qayta tayinlashni taqiqlaydi, ichidagi massiv yoki obyektni emas:

ts
class Menu {
  readonly dishes: string[] = ["Osh"];
}
const menu = new Menu();
menu.dishes.push("Manti");
console.log(menu.dishes); // [ 'Osh', 'Manti' ]

tsc — xatosiz. menu.dishes = [] taqiqlangan, lekin push — massivning metodi, maydonga yozish emas. Massiv ham o'zgarmasin desangiz, uning turini ham readonly qiling: readonly dishes: readonly string[]. Unda push ham TS2339 beradi (Massiv va tuple turlari).

5. Parameter properties — va nega kanonda yo'q

5.1 Qisqa yozuv

Maydonni e'lon qilib, keyin konstruktorda tayinlash — takror. TypeScript'da buning qisqa yozuvi bor — parameter properties (parametr xususiyatlari):

ts
class Guest {
  constructor(
    public readonly name: string,
    private phone: string,
  ) {}

  masked(): string {
    return `${this.name}: ***${this.phone.slice(-2)}`;
  }
}

const guest = new Guest("Malika", "+998 90 000 00 00");
console.log(guest.masked()); // Malika: ***00

Konstruktor parametri oldidagi modifikator (public, private, protected, readonly) TypeScript'ga aytadi: "shu nomli maydon yarat va parametrni unga yoz". Tana bo'sh ({}), lekin guest.name bor.

5.2 Bu sintaksis JavaScript'ga kod qo'shadi

tsc bu klassni shunday JavaScript'ga aylantiradi:

js
class Guest {
    name;
    phone;
    constructor(name, phone) {
        this.name = name;
        this.phone = phone;
    }
    // …
}

Ko'rdingizmi: ikki maydon e'loni va ikki tayinlash qatori paydo bo'ldi. Ularni turlarni o'chirib olib bo'lmaydi — yangi kod yozish kerak. Odatdagi turlar (: string, private, readonly) esa shunchaki o'chiriladi.

TypeScript'ni o'rnatish darsida ko'rganingizdek, Node 24 .ts faylni turlarini o'chirib ishga tushiradi (type stripping). Kod qo'shish kerak bo'lgan sintaksisni u bajarmaydi. Yuqoridagi klassni node guest.ts bilan ishga tushirsak:

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

Tarjimasi: "TypeScript parameter property faqat-o'chirish rejimida qo'llab-quvvatlanmaydi". enum bilan ham xuddi shu xato chiqadi — buni keyingi darsda to'liq ko'ramiz.

5.3 erasableSyntaxOnly: xatoni oldindan ko'rish

Node'da yiqilishni kutmasdan, tsc ning o'zi bunday sintaksisni taqiqlashi mumkin. Buning uchun erasableSyntaxOnly ("faqat o'chiriladigan sintaksis") bayrog'i bor:

ts
// @ts-options: {"erasableSyntaxOnly": true}
class Guest {
  constructor(
    public readonly name: string,
    private phone: string,
  ) {}
}

Kompilyator:

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

Tarjimasi: "erasableSyntaxOnly yoqilganda bu sintaksisga ruxsat yo'q". Har parametr uchun bittadan. Mashqlar papkangizdagi tsconfig.json da ham, vazifalar da ham bu bayroq yoqilgan: testlar Node'da .ts dan to'g'ridan-to'g'ri ishlaydi. Shuning uchun kanon qoidasi: maydonlar oddiy e'lon bilan yoziladi, parameter properties ishlatilmaydi.

Parameter properties'ni ko'p mavjud loyihalarda uchratasiz — masalan, Angular va NestJS (keyingi qismlarda o'rganiladigan framework'lar) ularga tayanadi. O'qiy olish shart, yangi kodda esa bayroqqa qarab tanlanadi.

Tekshirib ko'ring: Qaysi biri erasableSyntaxOnly bilan xato beradi: private x = 1; (maydon), readonly y: number; (maydon), constructor(readonly z: number) {}?

Javob

Faqat uchinchisi. private va readonly maydon oldida — tur ma'lumoti, ularni o'chirsa oddiy maydon qoladi. Konstruktor parametridagi readonly esa maydon yaratadi — bu yangi kod, o'chirib bo'lmaydi.

6. implements: klass va interfeys shartnomasi

6.1 Klass va'dani bajaradimi?

interface ni type va interface darsida obyekt shakli sifatida o'rgandik. Klass ham shu shaklga mos kelishini va'da qila oladi:

ts
interface Printable {
  copies: number;
  print(): string;
}
class Receipt implements Printable {
  print(): string {
    return "Chek: 63 000 so'm";
  }
}

Kompilyator:

text
TS2420: Class 'Receipt' incorrectly implements interface 'Printable'.
  Property 'copies' is missing in type 'Receipt' but required in type 'Printable'.

Tarjimasi: "Receipt klassi Printable interfeysini noto'g'ri amalga oshiradi: Receipt da copies yo'q, Printable esa uni talab qiladi". implements — shartnoma: klass interfeysdagi hamma narsani bajaradi deb aytadi, tsc esa shuni tekshiradi.

Klass bir nechta interfeysni amalga oshira oladi — vergul bilan: class Receipt implements Printable, Named. Bu Kengaytirish darsidagi interface extends A, B ga o'xshaydi.

6.2 Tuzoq: implements turlarni bermaydi

Interfeys metod parametrining turini aytadi. Klass metodida uni qayta yozmasak-chi?

ts
interface Discount {
  apply(price: number): number;
}
class HalfPrice implements Discount {
  apply(price) {
    return price / 2;
  }
}

Kompilyator:

text
TS7006: Parameter 'price' implicitly has an 'any' type.

Tarjimasi: "price parametrining turi yashirincha any bo'lib qoldi". TypeScript aytyapti: "bu parametrga tur yozmadingiz, men esa uni o'zim topa olmadim". implements faqat tekshiradi — klassga tur bermaydi. price ning turi interfeysdan olinmaydi, shuning uchun yashirin any. Klass metodlarida parametr turlarini har doim yozing: apply(price: number): number.

Shuning uchun implements ni ixtiyoriy deb bilish kerak: Strukturaviy tiplash tufayli HalfPrice obyekti Discount ga implements siz ham mos keladi. implements ning foydasi — xato klassning o'zida chiqadi, uni ishlatgan joyda emas.

7. abstract klass: chala qolip

7.1 O'zidan obyekt yasab bo'lmaydi

«Bahor» ikki xil to'lov qabul qiladi: naqd va karta. Ikkalasida summa va jami hisoblash bir xil, faqat komissiya har xil. Umumiy qismni ota klassga olamiz, komissiyani esa har bola klass o'zi yozsin:

ts
abstract class Payment {
  readonly amount: number;
  constructor(amount: number) {
    this.amount = amount;
  }
  abstract fee(): number;
  total(): number {
    return this.amount + this.fee();
  }
}

class Cash extends Payment {
  fee(): number {
    return 0;
  }
}

class Card extends Payment {
  static readonly RATE = 0.01;
  fee(): number {
    return Math.round(this.amount * Card.RATE);
  }
}

const payments: Payment[] = [new Cash(63000), new Card(63000)];
console.log(payments.map((p) => p.total())); // [ 63000, 63630 ]
  • abstract class — abstrakt klass: undan to'g'ridan-to'g'ri new qilib bo'lmaydi, u faqat ota sifatida ishlatiladi. Kundalik o'xshatish: blanka qolipi — uni to'ldirib bo'lmaydi, avval nusxa olinadi.
  • abstract fee(): number; — tanasiz metod: "har bola klass buni yozishi shart".
  • total() — oddiy metod, hamma bolalarga umumiy. U hali yozilmagan fee() ni chaqira oladi.

Ikkita qoida buzilishi:

ts
abstract class Payment {
  abstract fee(): number;
}
class Transfer extends Payment {}
const p = new Payment();

Kompilyator:

text
TS2515: Non-abstract class 'Transfer' does not implement inherited abstract member fee from class 'Payment'.
TS2511: Cannot create an instance of an abstract class.
  • TS2515: "abstrakt bo'lmagan Transfer klassi Payment dan meros qolgan abstrakt fee a'zosini amalga oshirmagan".
  • TS2511: "abstrakt klassdan nusxa (instance) yaratib bo'lmaydi".

Mana shu ierarxiya chizmada. Strelka uchi bo'sh uchburchak — "meros oladi":

classDiagram
  class Payment {
    <<abstract>>
    +amount number
    +fee() number
    +total() number
  }
  class Cash {
    +fee() number
  }
  class Card {
    +RATE number
    +fee() number
  }
  Payment <|-- Cash
  Payment <|-- Card

7.2 abstract va interface — qachon qaysi?

interface abstract class
Umumiy kod (total()) yo'q, faqat shakl bor
Ishlash paytida yo'q (o'chiriladi) haqiqiy klass
Bir nechtasidan implements A, B faqat bitta extends
instanceof bilan tekshirish yo'q bor

Faqat shakl kerak bo'lsa — interface. Bola klasslar umumiy kodni ham bo'lishsin desangiz — abstract class. abstract so'zi faqat tur: JavaScript'da u oddiy klass bo'lib qoladi, shuning uchun erasableSyntaxOnly unga ruxsat beradi.

8. override: "men otaning metodini almashtiryapman"

Bola klass ota metodini qayta yozsa, oldiga override qo'yish mumkin. noImplicitOverride ("yashirin override yo'q") bayrog'i bilan esa — shart:

ts
// @ts-options: {"noImplicitOverride": true}
class Notifier {
  send(text: string): string {
    return text;
  }
}
class SmsNotifier extends Notifier {
  send(text: string): string {
    return `SMS: ${text}`;
  }
  override sned(text: string): string {
    return text;
  }
}

Kompilyator:

text
TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Notifier'.
TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'Notifier'.
  • TS4114: "bu a'zo ota klassdagi a'zoni almashtiradi, shuning uchun override bo'lishi shart".
  • TS4113: "bu a'zoda override bo'lishi mumkin emas, chunki ota klassda bunday a'zo yo'q". sned — harf xatosi. override siz bu yangi metod bo'lib qolardi va send jim almashtirilmasdi.

Ikkala xabar ham bitta xavfdan saqlaydi: ota klassdagi metod nomi o'zgarsa yoki bolada xato yozilsa, aloqa jim uziladi. vazifalar bu bayroqni Strict bayroqlar oilasi qadamida yoqadi (o'lchovda 0 xato).

9. Ko'p uchraydigan xatolar

9.1 Maydonni e'lon qilmasdan this.x = …

TS2339: Property 'x' does not exist on type 'Order'. Tuzatish: klass tanasida x: Tur; e'lon qiling.

9.2 Maydon qiymatsiz qoldi

TS2564. Tuzatish: boshlang'ich qiymat, konstruktorda tayinlash yoki | undefined. ! — faqat oxirgi chora.

9.3 private ni xavfsizlik deb o'ylash

private faqat tsc ga ko'rinadi: obj["x"], JSON.stringify, JavaScript fayllari uni ko'radi. Tuzatish: haqiqiy yopiqlik — #.

9.4 erasableSyntaxOnly loyihasida parameter properties

TS1294, Node'da ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX. Tuzatish: oddiy maydon e'loni va konstruktorda tayinlash.

9.5 implements dan tur kutish

TS7006 — metod parametri any. Tuzatish: klass metodlarida parametr turlarini qayta yozing.

9.6 .ts faylda JSDoc tur

/** @type {Vazifa[]} */ #v = []; — .ts da never[]. Tuzatish: #v: Vazifa[] = [];.

10. Mashqlar

1-mashq (oson): Qaysi qator xato?

ts
class Booking {
  static readonly OPEN_HOUR = 10;
  readonly guest: string;
  protected people: number;
  #table = 0;
  constructor(guest: string, people: number) {
    this.guest = guest;
    this.people = people;
  }
  seat(table: number): void {
    this.#table = table;
  }
}
const b = new Booking("Dilshod aka", 4);
b.seat(7);
console.log(b.guest, Booking.OPEN_HOUR); // Dilshod aka 10

Quyidagilarning qaysi biri tsc xatosi beradi? b.guest = "Ali" — xato kodi: [:TS2540]. b.people — xato kodi: [:TS2445]. b["people"] — xato bormi (ha/yo'q): .

Yechim
  • b.guest = … — TS2540: readonly maydon konstruktordan keyin o'zgarmaydi.
  • b.people — TS2445: protected faqat klass va bola klasslar ichida.
  • b["people"] — xato yo'q: kvadrat qavs private/protected ni chetlab o'tadi. Shuning uchun haqiqiy yopiqlik kerak bo'lsa — #table kabi.

2-mashq (o'rta): Kanon uslubiga o'tkazing

Bu klass parameter properties bilan yozilgan. Mashqlar papkangizdagi sozlamada (erasableSyntaxOnly) u TS1294 beradi, Node esa uni bajarmaydi:

ts
// @ts-options: {"erasableSyntaxOnly": true}
class Waiter {
  constructor(
    public readonly name: string,
    private tables: number[],
  ) {}
  serves(table: number): boolean {
    return this.tables.includes(table);
  }
}

Kompilyator:

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

Ikki parametr — ikki xato: ikkalasi ham maydon yaratadi. Uni o'chiriladigan sintaksis bilan qayta yozing: oddiy maydonlar, readonly nom va haqiqatan yopiq stollar ro'yxati (#). Qo'shimcha: stollar tashqaridan o'zgarmasligi uchun konstruktor massivning nusxasini olsin.

Yechim
ts
// @ts-options: {"erasableSyntaxOnly": true}
class Waiter {
  readonly name: string;
  readonly #tables: readonly number[];

  constructor(name: string, tables: readonly number[]) {
    this.name = name;
    this.#tables = [...tables];
  }

  serves(table: number): boolean {
    return this.#tables.includes(table);
  }
}

const tables = [1, 2, 3];
const sardor = new Waiter("Sardor", tables);
tables.push(9);
console.log(sardor.serves(2), sardor.serves(9)); // true false
  • readonly #tables: readonly number[] — maydon ham, massiv ham o'zgarmaydi (readonly sayoz bo'lgani uchun ikkalasi kerak).
  • [...tables] — nusxa: tashqaridagi tables.push(9) ofitsiantning ro'yxatiga ta'sir qilmadi.
  • Bayroq blokning birinchi qatorida yoqilgan — tsc xatosiz o'tkazdi.

3-mashq (qiyin): Bildirishnomalar

«Bahor» mehmonga buyurtma tayyor bo'lganini SMS yoki Telegram orqali xabar qiladi. Quyidagilarni yozing:

  • interface Named { readonly name: string } va interface Sender { send(text: string): string }.
  • abstract class Notifier implements Named, Sender: readonly name, yopiq #sent hisoblagich, abstrakt send, oddiy notify(text) — hisoblagichni oshirib, "1. <send natijasi>" qaytarsin.
  • SmsNotifier — telefon raqami (readonly #phone), xabarni 20 belgigacha qisqartiradi (SMS qisqa). TelegramNotifier — matnni to'liq yuboradi. Ikkalasida override.
Yechim
ts
// @ts-options: {"noImplicitOverride": true}
interface Named {
  readonly name: string;
}
interface Sender {
  send(text: string): string;
}

abstract class Notifier implements Named, Sender {
  readonly name: string;
  #sent = 0;
  constructor(name: string) {
    this.name = name;
  }
  abstract send(text: string): string;
  notify(text: string): string {
    this.#sent++;
    return `${this.#sent}. ${this.send(text)}`;
  }
}

class SmsNotifier extends Notifier {
  readonly #phone: string;
  constructor(phone: string) {
    super("SMS");
    this.#phone = phone;
  }
  override send(text: string): string {
    return `${this.#phone} ga: ${text.slice(0, 20)}`;
  }
}

class TelegramNotifier extends Notifier {
  constructor() {
    super("Telegram");
  }
  override send(text: string): string {
    return `Telegram: ${text}`;
  }
}

const list: Notifier[] = [
  new SmsNotifier("+998 90 000 00 00"),
  new TelegramNotifier(),
];
for (const n of list) {
  console.log(n.notify("Buyurtmangiz tayyor, 5-stol"));
}
console.log(list[1]?.notify("Rahmat!"));

Konsolda:

text
1. +998 90 000 00 00 ga: Buyurtmangiz tayyor,
1. Telegram: Buyurtmangiz tayyor, 5-stol
2. Telegram: Rahmat!
  • #sent har obyektda alohida: SMS — 1, Telegram — avval 1, keyin 2.
  • implements Named, Sender abstrakt klassda ham ishlaydi: send abstrakt bo'lsa ham, shartnoma bajarildi deb hisoblanadi — bolalar uni yozishga majbur.
  • list[1]?.notify — massiv elementi undefined bo'lishi mumkin, ?. buni hisobga oladi (null va undefined).
  • super("SMS") — bola konstruktori avval ota konstruktorini chaqiradi (Meros). this.#phone faqat undan keyin.

4-mashq: Vazifalar qadami — klass modullari .ts ga

vazifalar dagi uchta klass moduli .ts ga o'tadi: vazifa.js (Vazifa, VazifaXatosi), royxat.js (VazifalarRoyxati), prefiks-daraxti.js (PrefiksDaraxti). Klass xulqi o'zgarmaydi — faqat turlar.

  • Branch: refactor/klasslar-ts
  • Commit: refactor: Vazifa, VazifalarRoyxati, PrefiksDaraxti — .ts, readonly, tanasi: # — ish vaqtida yopiq, readonly — tsc; parametr xususiyatlari yo'q
  1. git mv bilan uchala faylni .ts ga o'tkazing va npx tsc --noEmit ni ishga tushiring. Nechta xato chiqdi, qaysi kodlar ko'p?
  2. Import yo'llari: asosiy.js, holat.ts, paket.js, render.js, saqlash.ts va 7 ta test faylida ./vazifa.js → ./vazifa.ts (va qolgan ikkitasi). sw.js da VERSIYA = "v5-4".
  3. JSDoc turlarini TypeScript sintaksisiga o'tkazing: maydonlar e'loni, parametrlar va natija turlari. MAKS_UZUNLIK — static readonly, id — readonly, #matn — # qoladi. Parameter properties — yo'q.
  4. Tekshiruv: npm run build, npm test, npm run tip, npm run lint, npm run format:check.
Yechim

1. Faqat nomi o'zgartirilganda (tsc 7.0.2) — 53 ta xato:

3 klass moduli .ts ga: 53 xato kodlar bo'yicha
  • TS7006 (any parametr)35 xato
  • TS2339 (yo'q xususiyat)11 xato
  • TS2345 (argument)3 xato
  • TS18046 (unknown)2 xato
  • TS2349 + TS24882 xato

Manba: O'lchandi: tsc 7.0.2 --noEmit, vazifalar holatlar/15 → vazifa/royxat/prefiks-daraxti .ts, Node 24.21.0, 2026-10-06

Ko'pi — TS7006: JSDoc'dagi @param {number} id endi hisobga olinmaydi, parametrlar any. TS2339 lar ikki xil:

text
assets/js/vazifa.ts(14,10): error TS2339: Property 'kod' does not exist on type 'VazifaXatosi'.
assets/js/royxat.ts(131,42): error TS2339: Property 'id' does not exist on type 'never'.
assets/js/royxat.ts(176,49): error TS2349: This expression is not callable.
  Type 'never' has no call signatures.

Birinchisi — «Maydonlarni e'lon qilish» bo'limidagi holat: this.kod e'lonsiz. Qolgan 10 tasi va TS2349 — #vazifalar = [] va #tinglovchilar = []: JSDoc'dagi @type yo'qoldi, bo'sh massiv never[] bo'ldi. Uning elementida hech qanday xususiyat yo'q, chaqirib ham bo'lmaydi.

2–3. Turlar yozilgach — vazifa.ts:

ts
// vazifa.ts — bitta vazifa va vazifalar xatosi
import type { VazifaMalumoti, VazifaXatosiKodi } from "./turlar.ts";

export class VazifaXatosi extends Error {
  readonly kod: VazifaXatosiKodi;

  constructor(
    xabar: string,
    sozlama?: ErrorOptions & { kod?: VazifaXatosiKodi },
  ) {
    super(xabar, sozlama);
    this.name = "VazifaXatosi";
    this.kod = sozlama?.kod ?? "NOMALUM";
  }
}

export class Vazifa {
  // index.html dagi maxlength bilan bir xil bo'lsin
  static readonly MAKS_UZUNLIK = 100;

  // readonly — faqat konstruktorda yoziladi (tsc tekshiradi, JS'da
  // yo'qoladi). #matn esa ish vaqtida ham yopiq: tashqaridan faqat
  // getter va matnniOzgartir (tekshiruv bilan)
  readonly id: number;
  bajarildi: boolean;
  #matn: string;

  constructor(id: number, matn: string, bajarildi = false) {
    this.id = id;
    this.#matn = matn;
    this.bajarildi = bajarildi;
  }

  static dan(malumot: VazifaMalumoti): Vazifa {
    return new Vazifa(malumot.id, malumot.matn, malumot.bajarildi);
  }

  get matn(): string {
    return this.#matn;
  }

  /** @returns matn qabul qilindimi */
  matnniOzgartir(yangi: string): boolean {
    const toza = yangi.trim();
    if (toza === "" || toza.length > Vazifa.MAKS_UZUNLIK) {
      return false;
    }
    this.#matn = toza;
    return true;
  }

  almashtir(): void {
    this.bajarildi = !this.bajarildi;
  }

  toJSON(): VazifaMalumoti {
    return {
      id: this.id,
      matn: this.#matn,
      bajarildi: this.bajarildi,
    };
  }
}
  • ErrorOptions & { kod?: … } — kesishma: standart cause va bizning kod.
  • readonly id va #matn — ikki xil himoya, ataylab. id ni o'qish hamma joyda kerak, faqat o'zgarmasin — tsc yetarli. matn ni esa faqat tekshiruvdan o'tgan holda o'zgartirish mumkin — bu ish vaqtidagi qoida, # kerak.
  • bajarildi = false — standart qiymatdan tur chiqarildi (boolean).

royxat.ts da maydonlar endi tur bilan: #vazifalar: Vazifa[] = [], #tinglovchilar: Array<() => void> = [], iterator — *[Symbol.iterator](): Generator<Vazifa, void, undefined> (burchak qavsdagi turlarni Generics darsida o'qishni o'rganamiz). prefiks-daraxti.ts da @typedef Tugun → type Tugun, readonly #ildiz va bitta haqiqiy o'zgarish — JSDoc assertion o'rniga tekshiruv:

ts
// Oldin (JS):
// const [tugun, soz] = /** @type {[Tugun, string]} */ (stek.pop());
const stek: Array<[Tugun, string]> = [[boshi, prefiks]];
let element = stek.pop();
while (element !== undefined) {
  const [tugun, soz] = element;
  // …
  element = stek.pop();
}

stek.pop() — [Tugun, string] | undefined. Oldin "bu yerda undefined bo'lmaydi" deb va'da berilardi. Endi tekshiruvning o'zi siklning sharti: undefined kelsa, sikl tugaydi (null va undefined).

Commit va PR:

bash
git switch -c refactor/klasslar-ts
git mv assets/js/vazifa.js assets/js/vazifa.ts
git mv assets/js/royxat.js assets/js/royxat.ts
git mv assets/js/prefiks-daraxti.js assets/js/prefiks-daraxti.ts
# turlar, import yo'llari, sw.js VERSIYA
npm run build && npm test && npm run tip
npm run lint && npm run format:check
git add .
git commit -m "refactor: Vazifa, VazifalarRoyxati, PrefiksDaraxti — .ts, readonly" \
  -m "# — ish vaqtida yopiq, readonly — tsc; parametr xususiyatlari yo'q"
git push -u origin refactor/klasslar-ts
gh pr create --fill
gh pr merge --merge

Commit sarlavhasi — 68 belgi (≤ 72). Natija: npm test — 137/137 (testlar endi .ts modullarni Node 24 type stripping bilan import qiladi), tip, lint, format:check — toza. Brauzer tekshiruvi (dist/) — 159/159. Diff: 17 fayl, +156 −201 — kod qisqardi: JSDoc izohlaridagi turlar ixchamroq TypeScript sintaksisiga o'tdi.

11. Real ishda

  • Xizmat klasslari (Node backend, Angular, NestJS): class OrderService { constructor(private readonly repo: OrderRepo) {} } — parameter properties'ning eng ko'p uchraydigan joyi. Endi uni o'qiysiz va nega ba'zi loyihalarda taqiqlanganini bilasiz.
  • implements shartnoma sifatida — bir interfeysning bir nechta amalga oshirilishi: haqiqiy to'lov tizimi va testdagi soxtasi (FakePayment implements PaymentGateway).
  • abstract — kutubxonalarda "kengaytirish nuqtasi": siz faqat bitta metodni yozasiz, qolganini ota klass bajaradi.
  • Intervyu: "private va # farqi?", "abstract class va interface farqi?", "implements turlarni beradimi?" — TypeScript intervyusining klassik savollari.

Xulosa

  • TypeScript klassida maydonlar e'lon qilinadi (id: number;), strict esa ularning qiymat olishini tekshiradi (TS2564).
  • public/protected/private — faqat tsc tekshiruvi, ishlash paytida yo'q. Haqiqiy yopiqlik — JavaScript'ning #.
  • readonly — faqat konstruktorda yoziladi, lekin sayoz. static readonly — klass doimiysi.
  • Parameter properties JavaScript'ga kod qo'shadi: erasableSyntaxOnly (TS1294) va Node type stripping ularni taqiqlaydi — kanonda oddiy maydonlar.
  • implements — klass interfeysga mosligini tekshiradi, tur bermaydi. abstract class — umumiy kodli chala qolip. override + noImplicitOverride — ota bilan aloqa jim uzilmasin.

Keyingi dars: enum va uning muqobillari — enum qanday ishlaydi, nega Node uni bajarmaydi va as const obyekt + union bilan uni qanday almashtiramiz.

Manbalar

  • TypeScript Handbook: "Classes" (Fields, readonly, Member Visibility, Parameter Properties, abstract, implements, override) — typescriptlang.org/docs/handbook/2/classes.html
  • TSConfig: strictPropertyInitialization, noImplicitOverride, erasableSyntaxOnly — typescriptlang.org/tsconfig
  • TypeScript 5.8 Release Notes: --erasableSyntaxOnly — typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html
  • Node.js hujjatlari: "Modules: TypeScript" (type stripping, qo'llab-quvvatlanmaydigan sintaksis) — nodejs.org/api/typescript.html
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
TypeScript klasslari: private, readonly, abstract va implements — IlmHamroh