🤘
TypeScript
  • Full-Stack Web Developer
  • 📚Теорія
    • 1️⃣Знайомство з TypeScript
      • Особливості TypeScript
      • Встановлення TS, сервера і перший проєкт
      • Приклади string і number
    • 2️⃣Типи даних
      • Прості та складні типи даних
      • Інші типи даних (any, unknown, tuple, enum, union type, literal type)
      • Типи даних для методів та функцій (void, type, never, custom, optional, readonly)
    • 3️⃣Компілятор і дебагінг
      • Налаштування компілятора
      • Дебагінг
    • 4️⃣ООП, класи та інтерфейси
      • ООП і TypeScript
      • Класи та інтерфейси
      • UML та шаблони проєктування
    • 👷Практика
  • Про мене
    • Про мене
Powered by GitBook
On this page
  • Класи
  • Конструктор
  • Методи
  • Модифікатори доступу
  • Скорочення ініціалізації
  • Readonly
  • Наслідування
  • Getter/Setter
  • Статичні методи та властивості
  • Абстрактні класи та методи
  • Інтерфейси
  • Інтерфейси об'єктів
  • Інтерфейси класів
  • Readonly
  • Розширення інтерфейсів
  • Інтерфейси як тип функції
  • Опціональні параметри
  1. Теорія
  2. ООП, класи та інтерфейси

Класи та інтерфейси

Класи

Конструктор

Конструктор класу є спеціальним методом, який використовується для ініціалізації нового об'єкта класу. Конструктор дозволяє встановити значення властивостей об'єкта при його створенні.

TypeScript
class Person {
  name: string;

  // Конструктор класу Person
  constructor(param: string) {
    this.name = param;
  }
}

У цьому прикладі constructor(name: string) - це конструктор класу Person, який приймає рядок param як параметр і ініціалізує властивість name об'єкта класу з переданим значенням.

Коли ви створюєте новий об'єкт класу за допомогою оператора new, конструктор викликається автоматично, і ви можете передавати параметри конструктору для налаштування початкового стану об'єкта.

Методи

Метод класу - це функція, яка визначена в тілі класу і пов'язана з об'єктами, які створюються на основі цього класу. Метод класу використовується для визначення поведінки об'єктів даного класу.

Додамо до прикладу вказаного вище якісь методи. Один нехай повертає значення (яке ми покажемо через консоль), а другий формує і виводить повідомлення одразу.

TypeScript
class Person {
  name: string;

  // Конструктор класу Person
  constructor(name: string) {
    this.name = name;
  }

  // Метод класу для повернення імені
  getName(): string {
    return this.name;
  }

  // Метод класу для показу імені
  showName(): void {
    console.log("Your name: ", this.name);
  }
}

// Створення нового об'єкта класу Person
let person = new Person("John");

// Виклик методів класу
console.log(person.getName()); // Виведе "John"
person.showName(); // Виведе  "Your name: John"

І ось, що ми побачимо в консолі.

Ключове слово this - це посилання на контекст виконання. А оскільки наші методи будуть всередині створених обʼєктів на основі цього класу, то this.name - це посилання на властивість name всередині створеного обʼєкта.

Метод вашого класу можна скопіювати в який новий обʼєкт. допишемо до вищевказаного коду такі рядки. І подивимося, що покажеться у консолі.

TypeScript
let secondPerson = { showName: person.showName };
secondPerson.showName();

Як бачимо, в новий обʼєкт метод ми скопіювали, але сам параметр не задали. Тобто не задали контекст. Трохи доповнимо код:

TypeScript
let secondPerson = { name: "Sasha", showName: person.showName };
secondPerson.showName();

Модифікатори доступу

Модифікатори доступу - це ключові слова, які використовуються для визначення видимості класів, властивостей і методів в межах програми. Ці модифікатори вказують, які частини класу можуть бути доступні ззовні класу або обмежені в межах самого класу.

В TypeScript є три основні модифікатори доступу:

  • public - це як всі властивості та методи в js, можна викликати будь-де

  • private - не можна викликати ззовні екземпляра, не наслідується

  • protected - не можна викликати ззовні екземпляра, але наслідується

  1. Public (public): Якщо ви використовуєте public перед властивістю чи методом, це означає, що вони доступні ззовні класу.

TypeScript
class MyClass {
    public myProperty: number;
    public myMethod(): void {
        // код методу
    }
}

let obj = new MyClass();
obj.myProperty = 10;
obj.myMethod();
  1. Private (private): Якщо ви використовуєте private перед властивістю чи методом, це означає, що вони доступні лише всередині самого класу. Вони не можуть бути доступні або змінені ззовні класу.

TypeScript
class MyClass {
    private myProperty: number;
    private myMethod(): void {
        // код методу
    }
}

let obj = new MyClass();
// obj.myProperty = 10; // Помилка: немає доступу до приватної властивості
// obj.myMethod(); // Помилка: немає доступу до приватного методу
  1. Protected (protected): Якщо ви використовуєте protected перед властивістю чи методом, вони доступні в самому класі та в похідних класах. Це дозволяє спадкоємцям класу користуватися цими частинами класу, але не дозволяє зовнішнім об'єктам отримати до них доступ.

TypeScript
class MyClass {
    protected myProperty: number;
    protected myMethod(): void {
        // код методу
    }
}

class AnotherClass extends MyClass {
    constructor() {
        super();
        this.myProperty = 20; // Доступ до захищеної властивості відбувається через спадкування
        this.myMethod(); // Доступ до захищеного методу відбувається через спадкування
    }
}

Тобто, треба використовувати public - коли це інтерфейс взаємодії, private - коли це технічні методи або властивості, які не варто викликати поза класом, protected - коли це технічні методи або властивості, які не варто викликати поза, але при цьому вони повинні наслідуватися дочірніми.

Роблячи властивість приватною або захищеною ми уберігаємо її від прямої зміни. Для цього передбачають спеціальні методи для роботи зміни не на пряму. Наприклад:

TypeScript
class MyClass {
  private someMsg: string = "Hello";

  showMsg() {
    console.log(this.someMsg);
  }

  editMsg(txt) {
    this.someMsg = txt;
  }
}

let myObj = new MyClass();

У такому разі нам видасть помилку, якщо ми захочемо напряму змінити приватну властивість someMsg

Таким чином ми змушуємо розробників користуватися визначеними відкритими методами для зміни властивостей.

myObj.showMsg();
myObj.editMsg("My name is Sasha");
myObj.showMsg();

Скорочення ініціалізації

Як ми дотепер описували конструктор для створення обʼєктів на основі класу.

TypeScript
class Person {
  private firstName: string;
  private lastName: string;
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

Але у TypeScript існує зручний спосіб скороченої ініціалізації властивостей класу через конструктор. Цей підхід дозволяє визначити властивості класу прямо в параметрах конструктора.

TypeScript
class Person {
  // Скорочена ініціалізація властивостей через конструктор
  constructor(private firstName: string, private lastName: string) {}

  // Метод класу для виведення повного імені
  getFullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }
}

// Створення об'єкта класу Person за допомогою скороченої ініціалізації
let person = new Person("John", "Doe");

// Виклик методу класу
console.log(person.getFullName()); // Виведе "John Doe"

У цьому прикладі конструктор класу Person має два параметри (firstName і lastName), які одразу використовуються для ініціалізації приватних властивостей firstName і lastName. Це зручний підхід, який дозволяє визначити властивості класу та їхні значення в одному місці. При цьому ви не потребуєте додаткових рядків коду для присвоєння значень властивостям в тілі конструктора.

Readonly

Ключове слово readonly використовується для вказання, що властивість класу може бути прочитана, але не може бути змінена після її ініціалізації. Це робить властивість "тільки для читання".

TypeScript
class Circle {
  readonly radius: number;

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

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

let circle = new Circle(5);
console.log(circle.radius); // Виведе 5

// Спроба змінити значення radius призведе до помилки
// circle.radius = 10; // Помилка: властивість "radius" тільки для читанн

У цьому прикладі radius є властивістю класу Circle, і вона є readonly. Це означає, що після того, як значення radius встановлено у конструкторі, воно не може бути змінено в інших частинах класу або ззовні. Якщо ви спробуєте змінити значення radius поза конструктором або в інших методах класу, компілятор TypeScript виділить помилку. Це допомагає захистити дані від непередбачених змін.

Зазначте, що readonly можна використовувати не тільки для властивостей класів, але і для властивостей інших об'єктів в TypeScript.

Наслідування

У TypeScript, як і в інших об'єктно-орієнтованих мовах програмування, наслідування дозволяє одному класу (підкласу) успадковувати властивості і методи іншого класу (базового класу). Це означає, що підклас може використовувати властивості і методи базового класу, а також визначати власні властивості і методи.

Для створення успадкування в TypeScript використовується ключове слово extends.

TypeScript
// Базовий клас (суперклас)
class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  // Метод базового класу
  makeSound(): void {
    console.log("Some sound");
  }
}

// Підклас, який успадковує клас Animal
class Dog extends Animal {
  // Додаткова властивість підкласу
  breed: string;

  constructor(name: string, breed: string) {
    // Виклик конструктора базового класу за допомогою super()
    super(name);
    this.breed = breed;
  }

  // Перевизначений метод базового класу
  makeSound(): void {
    console.log("Bark!");
  }

  // Новий метод підкласу
  wagTail(): void {
    console.log("Tail is wagging");
  }
}

// Створення об'єкта підкласу
let myDog = new Dog("Buddy", "Golden Retriever");

// Виклик методів базового та підкласів
console.log(myDog.name); // Виведе "Buddy"
console.log(myDog.breed); // Виведе "Golden Retriever"
myDog.makeSound(); // Виведе "Bark!"
myDog.wagTail(); // Виведе "Tail is wagging"

У цьому прикладі клас Dog успадковує властивості та методи класу Animal. Ключове слово super використовується для виклику конструктора базового класу та доступу до його методів та властивостей (його потрібно викликати до будь-якої маніпуляції з this в конструкторі).

Успадкування дозволяє створювати ієрархії класів, де підкласи можуть розширювати або перевизначати поведінку базових класів в залежності від власних потреб.

Getter/Setter

Getter і Setter - це спеціальні методи класу в TypeScript (і в інших об'єктно-орієнтованих мовах програмування), які дозволяють отримувати (читати) та встановлювати (змінювати) значення властивостей об'єкта, відповідно. Це дозволяє контролювати доступ до властивостей і виконувати додаткові операції при їхньому зчитуванні або встановленні.

У TypeScript ви можете використовувати ключові слова get і set для визначення Getter і Setter, відповідно.

TypeScript
class Person {
  private name?: string;
  private surname?: string;

  constructor(name?: string, surname?: string) {
    this.name = name;
    this.surname = surname;
  }

  // Setter для встановлення значення імені
  set firstName(value: string) {
    this.name = value;
    console.log("Name is added/changed");
  }

  // Setter для встановлення значення прізвища
  set secondName(value: string) {
    this.surname = value;
    console.log("Surname is added/changed");
  }

  // Getter для отримання значення інформації про особу
  get information(): string {
    return this.name + " " + this.surname;
  }
}

// Створення об'єкта класу Person
let person = new Person("Isaak", "Newton");

// Використання Getter для отримання імені особи
console.log(person.information); // Виведе Isaak Newton

// Використання Setter для встановлення нового значення імені і прізвища
person.firstName = "Oleksandr";
person.secondName = "Khomiak";

// Використання Getter для отримання оновленого імені особи
console.log(person.information); // Виведе Oleksandr Khomiak

Синтаксис такий, ніби ми звертаємося до властивості, але по факту запускаються методи, які обробляють ці дані.

Статичні методи та властивості

Статичні методи та властивості в TypeScript належать не конкретному екземпляру класу, а самому класу. Це означає, що їх можна викликати або використовувати без створення об'єкта класу. Вони корисні, коли ви хочете виконати операції, які не пов'язані з конкретним екземпляром класу, або коли вам потрібно зберігати значення, які відносяться до самого класу, а не до його екземплярів.

Щоб створити властивість або метод статичним, просто після модифікатора вказуємо static.

Статичні методи

TypeScript
class MathOperations {
    static multiply(x: number, y: number): number {
        return x * y;
    }
}

// Виклик статичного методу без створення об'єкта класу
let result = MathOperations.multiply(5, 10);
console.log(result); // Виведе 50

У цьому прикладі multiply - це статичний метод класу MathOperations. Його можна викликати без створення об'єкта класу.

Статичні властивості

Статичні властивості працюють аналогічно статичним методам, але вони представляють собою значення, які відносяться до самого класу, а не до його екземплярів.

TypeScript
class AppConfig {
    static apiUrl: string = "https://api.example.com";
}

// Використання статичної властивості без створення об'єкта класу
console.log(AppConfig.apiUrl); // Виведе "https://api.example.com"

Абстрактні класи та методи

Абстрактні класи та методи в дозволяють визначити структуру класу, але не надають конкретну реалізацію для деяких частин цього класу. Абстрактні класи створюються за допомогою ключового слова abstract. Абстрактний метод - це метод, який визначається в абстрактному класі, але не має тіла (реалізації). Абстрактний клас може мати як абстрактні методи, так і звичайні методи з реалізацією.

Абстрактні класи

TypeScript
abstract class Shape {
    abstract calculateArea(): number; // Абстрактний метод без реалізації

    // Звичайний метод з реалізацією
    printArea(): void {
        console.log("Площа фігури: " + this.calculateArea());
    }
}

class Circle extends Shape {
    constructor(private radius: number) {
        super();
    }

    // Реалізація абстрактного методу
    calculateArea(): number {
        return Math.PI * this.radius * this.radius;
    }
}

let circle = new Circle(5);
circle.printArea(); // Виведе "Площа фігури: 78.54"

У цьому прикладі Shape - це абстрактний клас, який має абстрактний метод calculateArea(). Клас Circle успадковує абстрактний клас Shape і реалізує його абстрактний метод.

Абстрактні методи

Абстрактний метод - це метод без реалізації, який визначений в абстрактному класі і повинен бути реалізований в його похідних класах. Абстрактні методи позначаються ключовим словом abstract перед їхнім визначенням.

TypeScript
abstract class Animal {
    abstract makeSound(): void; // Абстрактний метод без реалізації
}

class Dog extends Animal {
    // Реалізація абстрактного методу
    makeSound(): void {
        console.log("Bark!");
    }
}

let dog = new Dog();
dog.makeSound(); // Виведе "Bark!"

У цьому прикладі Animal - це абстрактний клас з абстрактним методом makeSound(). Клас Dog успадковує Animal і реалізує його абстрактний метод.

Це дає нам контроль над реалізацією дочірніх класів, доки ми не реалізуємо свій метод у кожному з класів, то отримуватимемо помилку, і ми даємо гарантію, що у кожному дочірньому класі буде своя реалізація.

Інтерфейси

Інтерфейси - це спосіб визначення контракту для об'єктів. Вони використовуються для опису структури об'єктів та визначення, які властивості або методи повинні бути присутніми у класі або об'єкті, який реалізує цей інтерфейс. Чимось сзоді на абстрактні класи, от тільки зовсім не описують реалізацію, а тільки містять структуру.

Інтерфейси об'єктів

Інтерфейси об'єктів в TypeScript використовуються для визначення структури об'єктів. Вони визначають набір властивостей та їхні типи, які повинні бути присутніми в об'єкті, який реалізує цей інтерфейс. Це дозволяє вам створювати загальні конструкції для об'єктів та забезпечувати типовий контракт для їхньої структури.

TypeScript
interface Person {
  name: string;
  age: number;

  information(): void;
}

let person: Person = {
  name: "John",
  age: 30,

  information() {
    console.log("This is " + this.name + "! He is " + this.age + " years old.");
  },
};

person.information();

Інтерфейс описують після ключового слова interface. Назви інтерфейсам дають з великої літери. Тут інтерфейс працює як тип - він просто показує, які поля повинні бути в об'єкті.

Якщо об'єкт не має всіх властивостей, вказаних в інтерфейсі, TypeScript видасть помилку.

При роботі з обʼєктами інтерфейс можна записати як тип і функціонад буде такий само.

TypeScript
type Person = {
  name: string;
  age: number;

  information(): void;
};

let person: Person = {
  name: "John",
  age: 30,

  information() {
    console.log("This is " + this.name + "! He is " + this.age + " years old.");
  },
};

person.information();

Інтерфейси класів

Інтерфейси класів в TypeScript дозволяють визначити контракт для класів. Вони описують структуру класів, вказуючи наявність конкретних властивостей та методів, які повинні бути присутніми у класі, що реалізує інтерфейс. Інтерфейс класу виглядає аналогічно інтерфейсу об'єктів, але описує структуру класу замість структури об'єктів.

TypeScript
// інтерфейс 1
interface Person {
  name: string;
  age: number;

  information(): void;
}

// інтерфейс 2
interface Feedback {
  getName(): string;
}

// Клас
class TeamWorker implements Person, Feedback {
  constructor(public name: string, public age: number) {}

  information(): void {
    console.log("This is " + this.name + "! He is " + this.age + " years old.");
  }

  getName() {
    return this.name;
  }
}

// обʼєкт класу
let teamLead = new TeamWorker("Alex", 25);

teamLead.information(); // This is Alex! He is 25 years old.
console.log(teamLead.getName()); // "Alex"

Підключення інтерфейсів відбувається через команду implements і далі через кому ми передаємо будь-яку кількість інтерфейсів. А у класі будують логіку на цьому інтерфейсі.

Але сам клас ми можемо розширювати, додаючи йому нові методи та властивості (яких. немає в інтерфейсах), у цьому інтерфейси нас не обмежують. Наприклад можна ініціалізувати додаткову властивість, або описати додатковий метод.

constructor(public name: string, public age: number, public surnam?: string) {}
  setSurname(value: string) {
    this.surname = value;
  }

Readonly

В інтерфейсі не можна задавати модифікатори private, protected, оскільки вони всі вважаються як public (щоб можна було вказати private - для цього є абстрактний клас).

Але ми можемо задати readonly.

TypeScript
interface Person {
  readonly name: string;
}

const person: Person = {
  name: "Alex",
};

person.name = "Person name"; // Помилка, бо readonly

Розширення інтерфейсів

Можна розширювати інтерфейси за допомогою команди extends

TypeScript
interface Person {
  readonly name: string;
}

interface TeamWorker extends Person {
  readonly surName: string;

  showWorker();
}

Інтерфейси як тип функції

Замість типу функції її можна описати інтерфейсом. Функціонал один і той самий. Який вибрати - на вибір розробника.

TypeScript
//type AddFunc = (n1: number, n2: number) => number;
interface AddFunc {
  (n1: number, n2: number): number;
}

let add: AddFunc;

add = (n1:number, n2: number) => {
  return n1 + n2;
}

Опціональні параметри

В Інтерфейсах також можна використовувати опціональні параметри. Потрібно тільки вказати ? перед зазначенням типу.

TypeScript
interface Person {
  readonly name: string;
  readonly surName?: string;
}

PreviousООП і TypeScriptNextUML та шаблони проєктування

Last updated 1 year ago

У цьому прикладі apiUrl - це статична властивість класу AppConfig, яка має значення "". Цю властивість можна використовувати без створення об'єкта класу.

📚
4️⃣
https://api.example.com