Məzmuna keç
Educora
Orta17 dəq6 / 8

Siniflər və enum-lar

Siniflərə tipli sahələr əlavə et, `public`, `private`, `protected` və `readonly` ilə girişi idarə et, `implements` ilə interfeysə uyğunluğu yoxla və `enum`-u literal tiplərin birləşməsi ilə müqayisə et.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • Sinifdə sahələri tipləri ilə elan etmək
  • public, private, protected və readonly modifikatorlarını düzgün seçmək
  • implements ilə sinfin interfeysə uyğunluğunu yoxlamaq və parametr xassələrindən istifadə etmək
  • enum ilə literal tiplərin birləşməsi arasında seçim etmək

JavaScript kursunda BankAccount sinfini yazmışdıq. Orada bir zəif yer vardı: istənilən kod account.balance = 1000000 yaza bilərdi və buna heç nə mane olmurdu. TypeScript siniflərə üç şey əlavə edir: sahələrin tipləri, kimin nəyə müraciət edə biləcəyini təyin edən giriş modifikatorları və sinfin interfeysə uyğunluğunun yoxlanması. Dərsin sonunda isə sabit qiymətlər dəsti üçün iki yanaşmanı — enum-u və literal tiplərin birləşməsini — müqayisə edəcəyik.

Tipli sahələr

JavaScript-də sahəni konstruktorda this.owner = owner yazmaqla yaratmaq kifayət idi. TypeScript-də hər sahə sinfin gövdəsində tipi ilə birlikdə elan olunmalıdır. Elan etməsən, kompilyator error TS2339: Property 'owner' does not exist on type 'BankAccount'. xətasını verəcək. Metodların parametrləri və qaytardıqları qiymətlər adi funksiyalardakı kimi tiplənir.

TypeScript
class BankAccount {
  owner: string;
  balance: number;

  constructor(owner: string, balance = 0) {
    this.owner = owner;
    this.balance = balance;
  }

  deposit(amount: number): void {
    this.balance += amount;
  }
}

const account = new BankAccount('Aysel', 100);
account.deposit(50);
console.log(`${account.owner}: ${account.balance} AZN`);

account.balance = 1000000;
console.log(`${account.owner}: ${account.balance} AZN`);
Gözlənilən nəticə
Aysel: 150 AZN
Aysel: 1000000 AZN
Tiplər düzgündür, amma son iki sətir problemi göstərir: balansı kənardan istənilən qiymətə dəyişmək olur.

Giriş modifikatorları

ModifikatorKim müraciət edə bilər
publichamı; heç nə yazılmayanda susmaya görə belədir
privateyalnız həmin sinfin içindəki kod
protectedsinfin özü və ondan törəyən siniflər
readonlyhamı oxuya bilər, qiyməti yalnız elanda və ya konstruktorda vermək olar
TypeScript
class BankAccount {
  readonly owner: string;
  private balance: number;

  constructor(owner: string, balance = 0) {
    this.owner = owner;
    this.balance = balance;
  }
}

const account = new BankAccount('Aysel', 100);
account.balance = 1000000;
account.owner = 'Murad';
Text
account.ts:12:9 - error TS2341: Property 'balance' is private and only accessible within class 'BankAccount'.

12 account.balance = 1000000;
           ~~~~~~~

account.ts:13:9 - error TS2540: Cannot assign to 'owner' because it is a read-only property.

13 account.owner = 'Murad';
           ~~~~~
İndi balansı yalnız sinfin öz metodları dəyişə bilər, sahibin adı isə hesab açılandan sonra dəyişmir.

protected sahə kənardan gizlidir, amma törəmə siniflər onu görür. Əmanət hesabı faizi hesablamaq üçün valideynin balansına müraciət etməlidir:

TypeScript
class Account {
  protected balance = 0;

  deposit(amount: number): void {
    this.balance += amount;
  }
}

class SavingsAccount extends Account {
  addInterest(rate: number): number {
    this.balance += (this.balance * rate) / 100;
    return this.balance;
  }
}

const leyla = new SavingsAccount();
leyla.deposit(1000);
console.log(leyla.addInterest(5));
console.log(leyla.balance);
Text
savings.ts:19:19 - error TS2445: Property 'balance' is protected and only accessible within class 'Account' and its subclasses.

19 console.log(leyla.balance);
                     ~~~~~~~
SavingsAccount daxilində this.balance icazəlidir, kənarda leyla.balance isə yox.

implements və parametr xassələri

class Square implements Shape yazılışı kompilyatordan xahişdir: «Yoxla ki, bu sinifdə Shape interfeysinin tələb etdiyi hər şey var». Metodu unutsan, səhv interfeysdən istifadə olunan yerdə yox, elə sinfin özündə göstəriləcək:

TypeScript
interface Shape {
  name: string;
  area(): number;
}

class Circle implements Shape {
  name = 'circle';
  radius: number;

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

  area(): number {
    return Math.PI * this.radius ** 2;
  }
}

class Square implements Shape {
  name = 'square';
  side: number;

  constructor(side: number) {
    this.side = side;
  }
}
Text
shapes.ts:19:7 - error TS2420: Class 'Square' incorrectly implements interface 'Shape'.
  Property 'area' is missing in type 'Square' but required in type 'Shape'.

19 class Square implements Shape {
         ~~~~~~
TypeScript
class Circle {
  constructor(private radius: number, public label = 'circle') {}

  area(): number {
    return Math.PI * this.radius ** 2;
  }
}

const c = new Circle(2);
console.log(c.label, c.area().toFixed(2));
Gözlənilən nəticə
circle 12.57
İki sahə — radius və label — bir sətirdə elan olunub və doldurulub.

enum, yoxsa literal tiplər?

Enum (sadalama) bir-biri ilə əlaqəli adlı sabitlər dəstidir. Qiymət verilməsə, üzvlər 0-dan başlayaraq nömrələnir; sətir qiymətləri də vermək olar. Enum TypeScript-in tiplərdən əlavə JavaScript kodu yaradan nadir imkanlarındandır: kompilyator ondan real obyekt düzəldir.

TypeScript
enum Direction {
  Up,
  Down,
  Left,
  Right,
}

enum Status {
  Active = 'ACTIVE',
  Blocked = 'BLOCKED',
}

console.log(Direction.Left);
console.log(Direction[2]);
console.log(Status.Blocked);
Gözlənilən nəticə
2
Left
BLOCKED
Rəqəmli enum-da əks istiqamət də işləyir: Direction[2] adı qaytarır.
JavaScript
var Status;
(function (Status) {
    Status["Active"] = "ACTIVE";
    Status["Blocked"] = "BLOCKED";
})(Status || (Status = {}));
tsc enum Status-dan bu JavaScript kodunu yaradır — enum silinmir, proqramda qalır.

Çox vaxt eyni işi literal tiplərin birləşməsi görür. O, tamamilə silinir, qiymətlər adi sətirlərdir, səhvləri isə kompilyator eynilə tutur:

TypeScript
type Status = 'active' | 'blocked' | 'deleted';

function label(status: Status): string {
  switch (status) {
    case 'active':
      return 'Active user';
    case 'blocked':
      return 'Blocked';
    case 'deleted':
      return 'Deleted';
  }
}

console.log(label('blocked'));
label('banned');
Text
status.ts:15:7 - error TS2345: Argument of type '"banned"' is not assignable to parameter of type 'Status'.

15 label('banned');
         ~~~~~~~~
CəhətenumLiteral tiplərin birləşməsi
İcra zamanıreal obyekt yaradılırtamamilə silinir
Qiymətin yazılışıStatus.Blocked'blocked'
node file.ts (tiplərin silinməsi)dəstəklənmirişləyir
Bütün qiymətləri siyahı kimi almaqObject.values(Status) ilə mümkündürtipdən birbaşa mümkün deyil

Əsas fikirlər

  • TypeScript-də sinfin hər sahəsi gövdədə tipi ilə birlikdə elan olunur.
  • public — hamı, private — yalnız sinif, protected — sinif və törəmələri, readonly — yalnız oxumaq üçün.
  • private yalnız kompilyasiya zamanı yoxlanılır; icra zamanı gizlilik üçün # sahələri var.
  • implements sinfin interfeysə uyğunluğunu yoxlayır; constructor(private x: T) sahəni qısa yolla elan edir.
  • Enum real JavaScript obyekti yaradır; literal tiplərin birləşməsi isə silinir və əksər hallarda daha yaxşı seçimdir.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
Hansı modifikator sahəyə yalnız sinfin özündən və ondan törəyən siniflərdən müraciətə icazə verir?