Продолжая наше погружение в TypeScript, мы переходим к рассмотрению классов — мощного инструмента объектно-ориентированного программирования. В этой статье мы изучим основы создания классов в TypeScript, их свойства, методы, конструкторы, наследование, абстрактные классы и интерфейсы.
Создание класса в TypeScript
Для лучшего понимания давайте рассмотрим пример использования классов в контексте приложения для списка покупок.
class GroceryItem {
name: string;
quantity: number;
isPurchased: boolean;
constructor(name: string, quantity: number) {
this.name = name;
this.quantity = quantity;
this.isPurchased = false;
}
purchase() {
this.isPurchased = true;
}
}
// Создаем экземпляры класса GroceryItem
const milk = new GroceryItem('Молоко', 1);
const bread = new GroceryItem('Хлеб', 2);
// Покупаем продукты
milk.purchase();
console.log(milk); // Выведет: GroceryItem { name: 'Молоко', quantity: 1, isPurchased: true }
console.log(bread); // Выведет: GroceryItem { name: 'Хлеб', quantity: 2, isPurchased: false }
В этом примере мы создали класс GroceryItem, который представляет собой элемент списка покупок. Класс GroceryItem определен с тремя свойствами:
- name имеет тип string, что означает, что ожидается строковое значение (название продукта).
- quantity имеет тип number, что указывает на числовое значение (количество продукта).
- isPurchased имеет тип boolean, который представляет логическое значение (true или false), указывающее, куплен ли продукт.
Наследование в TypeScript
Наследование позволяет создавать новый класс на основе существующего, заимствуя его свойства и методы. Давайте рассмотрим пример наследования в контексте списка покупок.
class GroceryItem {
constructor(public name: string, public quantity: number, public isPurchased: boolean = false) {}
purchase() {
this.isPurchased = true;
}
}
class SpecialGroceryItem extends GroceryItem {
constructor(name: string, quantity: number, public discount: number) {
super(name, quantity);
}
applyDiscount() {
const discountedPrice = this.quantity * this.discount;
console.log(`Скидка: ${discountedPrice}`);
}
}
// Создаем экземпляр класса SpecialGroceryItem
const specialItem = new SpecialGroceryItem('Специальный товар', 3, 0.2);
specialItem.purchase();
specialItem.applyDiscount();
console.log(specialItem); // Выведет: SpecialGroceryItem { name: 'Специальный товар', quantity: 3, isPurchased: true, discount: 0.2 }
В этом примере мы создали класс SpecialGroceryItem, который наследует свойства и методы от класса GroceryItem. Класс SpecialGroceryItem добавляет новое свойство discount и метод applyDiscount(), который применяет скидку к товару.
Модификаторы доступа в TypeScript
Модификаторы доступа позволяют управлять доступом к свойствам и методам классов. TypeScript поддерживает три типа модификаторов доступа:
- public: Публичные члены класса доступны из любого места в программе. Это значит, что они могут быть доступны как внутри самого класса, так и вне его.
- private: Приватные члены класса доступны только внутри самого класса. Они не могут быть доступны извне класса или его наследников.
- protected: Защищенные члены класса доступны внутри самого класса и его подклассов (наследников), но не извне класса.
Рассмотрим пример для модификатора private:
class GroceryList {
private items: GroceryItem[] = [];
addItem(item: GroceryItem) {
this.items.push(item);
}
printList() {
this.items.forEach(item => console.log(item.name));
}
}
const groceryList = new GroceryList();
groceryList.addItem(new GroceryItem('Яблоки', 5));
groceryList.printList(); // Выведет: Яблоки
В этом примере мы использовали модификатор доступа private для свойства items класса GroceryList, что означает, что оно доступно только внутри самого класса. Это обеспечивает защиту данных и предотвращает их непосредственное изменение извне.
Пример для модификатора protected:
class GroceryList {
protected items: GroceryItem[] = [];
addItem(item: GroceryItem) {
this.items.push(item);
}
printList() {
this.items.forEach(item => console.log(item.name));
}
}
class SpecialGroceryList extends GroceryList {
applyDiscount() {
this.items.forEach(item => {
// Класс SpecialGroceryList имеет доступ к защищенному свойству items
console.log(`Применение скидки к: ${item.name}`);
});
}
}
const groceryList = new SpecialGroceryList();
groceryList.addItem(new GroceryItem('Яблоки', 5));
groceryList.printList(); // Выведет: Яблоки
groceryList.applyDiscount(); // Применение скидки к: Яблоки
В этом примере свойство items класса GroceryList помечено модификатором protected, что означает, что оно доступно как внутри самого класса, так и в его подклассах (в данном случае SpecialGroceryList). Таким образом, подкласс SpecialGroceryList может использовать метод printList() для вывода списка продуктов и даже обращаться к защищенному свойству items для применения каких-либо дополнительных операций, например, применения скидки.
Статические свойства и методы в TypeScript
Статические свойства и методы предоставляют возможность создавать свойства и методы, которые принадлежат самому классу, а не его экземплярам. Это позволяет использовать класс как пространство имен для функциональности, которая не зависит от конкретных экземпляров.
Давайте рассмотрим пример статического метода в классе GroceryItem, который считает общее количество всех продуктов в списке.
class GroceryItem {
static totalItems: number = 0;
constructor(public name: string, public quantity: number, public isPurchased: boolean = false) {
GroceryItem.totalItems++;
}
purchase() {
this.isPurchased = true;
}
static getTotalItems() {
return GroceryItem.totalItems;
}
}
// Создаем экземпляры класса GroceryItem
const apple = new GroceryItem('Яблоки', 3);
const banana = new GroceryItem('Бананы', 2);
console.log(GroceryItem.getTotalItems()); // Выведет: 2
В этом примере мы создали статическое свойство totalItems, которое хранит общее количество всех продуктов в списке. Мы также создали статический метод getTotalItems(), который возвращает это общее количество.
Статические свойства и методы могут быть использованы без создания экземпляра класса, что делает их полезными для функциональности, которая не зависит от конкретных объектов.
Применение статических методов и свойств
Рассмотрим пример, где мы используем статические методы и свойства для работы с данными о продуктах без создания экземпляров класса.
class GroceryItem {
static totalItems: number = 0;
static items: GroceryItem[] = [];
constructor(public name: string, public quantity: number, public isPurchased: boolean = false) {
GroceryItem.totalItems++;
GroceryItem.items.push(this);
}
purchase() {
this.isPurchased = true;
}
static getTotalItems() {
return GroceryItem.totalItems;
}
static printAllItems() {
GroceryItem.items.forEach(item => console.log(item.name));
}
}
// Создаем экземпляры класса GroceryItem
const orange = new GroceryItem('Апельсины', 4);
const pear = new GroceryItem('Груши', 5);
console.log(GroceryItem.getTotalItems()); // Выведет: 2
GroceryItem.printAllItems(); // Выведет: Апельсины, Груши
В этом примере мы используем статические свойства totalItems и items, чтобы отслеживать общее количество продуктов и хранить список всех продуктов в списке. Мы также создали статический метод printAllItems(), который выводит все названия продуктов.