Мы с тобой уже прошли большой путь, разобрались с базовыми типами, функциями, интерфейсами и другими кирпичиками языка. Но сегодня нас ждет один из самых важных и фундаментальных уроков во всем курсе. Мы будем говорить о классах.
Почему он так важен? Потому что классы это краеугольный камень объектно-ориентированного программирования (ООП). Они позволяют структурировать код, создавать сложные, многократно используемые сущности и строить по-настоящему масштабируемые приложения. TypeScript, будучи надмножеством JavaScript, предоставляет потрясающие возможности для работы с классами, добавляя к ним строгую типизацию, которой так не хватает в чистом JS.
В этом уроке мы детально разберем синтаксис классов в TypeScript, научимся объявлять свойства, познакомимся с конструктором, специальным методом для инициализации объектов и создадим наши первые методы. Всё это мы будем подкреплять практическими примерами и задачами.
Синтаксис классов в TypeScript
Давай начнем с самого простого, с того, как вообще объявить класс. Синтаксис TypeScript для классов очень похож на современный JavaScript (ES6 и выше), но с одним критически важным дополнением, аннотациями типов.
Базовый синтаксис объявления класса выглядит так:
class ClassName { // свойства класса (поля) propertyName: type; // конструктор constructor(parameters) { // инициализация свойств } // методы класса methodName(parameters): returnType { // тело метода } }
Ключевое слово class сообщает компилятору, что мы начинаем объявление нового класса. За ним следует имя класса. По общепринятому соглашению, имена классов в TypeScript и JavaScript пишутся в PascalCase (каждое слово с заглавной буквы), чтобы отличать их от переменных и функций, которые обычно используют camelCase.
Давай создадим наш первый, максимально простой класс. Представь, что мы пишем приложение для зоопарка. Создадим класс Animal.
class Animal { // Пока что класс пустой, но это уже валидный код. }
Теперь мы можем создавать экземпляры (объекты) этого класса с помощью оператора new:
let lion = new Animal(); let tiger = new Animal(); console.log(lion); // Animal {} console.log(tiger); // Animal {}
lion и tiger это два разных объекта, созданных по одному и тому же «чертежу», классу Animal. Пока наш класс не делает ничего полезного, давай это исправлять, добавляя ему свойства.
Аннотация типов для свойств класса: описываем состояние объекта
Свойства класса (их также называют полями) это переменные, которые принадлежат каждому экземпляру класса. Они описывают состояние объекта. У каждого животного в нашем зоопарке есть имя, возраст и вид. Давай опишем это в классе.
В TypeScript мы обязаны явно объявить все свойства класса и указать их типы. Это можно сделать двумя основными способами:
-
Явное объявление перед конструктором.
-
Объявление и инициализация прямо в конструкторе (с использованием параметров свойств).
Сначала рассмотрим первый, самый распространенный способ.
Мы перечисляем свойства в теле класса, до определения конструктора и методов.
class Animal { // Объявление свойств с аннотациями типов. name: string; age: number; species: string; isMammal: boolean; // новое свойство // Конструктор пока пустой constructor() {} }
Обрати внимание: мы только объявили свойства, но не инициализировали их. Если мы сейчас создадим объект, то свойства будут иметь специальное значение undefined.
let unknownAnimal = new Animal(); console.log(unknownAnimal.name); // undefined console.log(unknownAnimal.age); // undefined
В чистом JavaScript это норма, но TypeScript, в своем строгом режиме (strict: true, который настоятельно рекомендуется), будет ругаться на попытку использовать неинициализированное свойство. Компилятор скажет нам: Property 'name' has no initializer and is not definitely assigned in the constructor. Он переживает, что мы забыли присвоить значение и мы можем столкнуться с ошибкой во время выполнения.
Как же нам правильно проинициализировать свойства? Для этого существует волшебный метод, конструктор.
Конструктор класса: место, где рождается объект
Конструктор это специальный метод класса, который автоматически вызывается при создании нового экземпляра класса с помощью оператора new. Его основная задача инициализировать объект, то есть присвоить начальные значения его свойствам.
В TypeScript конструктор объявляется с помощью ключевого слова constructor. Он может принимать параметры, которые мы передаем при вызове new ClassName(...).
Давай модифицируем наш класс Animal, добавив конструктор, который будет принимать значения для свойств и присваивать их.
class Animal { name: string; age: number; species: string; isMammal: boolean; // Конструктор с параметрами constructor(animalName: string, animalAge: number, animalSpecies: string, mammalStatus: boolean) { // Инициализация свойств класса this.name = animalName; this.age = animalAge; this.species = animalSpecies; this.isMammal = mammalStatus; console.log(`Новое животное ${this.name} создано!`); } }
Разберем каждую строчку:
-
constructor(animalName: string, ...)мы объявляем конструктор, который ожидает четыре аргумента с конкретными типами. -
thisключевое словоthisвнутри класса ссылается на текущий экземпляр класса. То есть на тот конкретный объект, который создается в данный момент. -
this.name = animalName;мы берем параметрanimalName, который передали в конструктор и присваиваем его свойствуnameтекущего объекта (this).
Теперь мы можем создавать животных осмысленно:
// Передаем аргументы в конструктор let myLion = new Animal("Симба", 5, "Лев", true); let myFrog = new Animal("Кваки", 1, "Лягушка", false); console.log(myLion); // Animal { name: 'Симба', age: 5, species: 'Лев', isMammal: true } console.log(myFrog.name); // "Кваки" console.log(myFrog.isMammal); // false
Гораздо лучше! Теперь у каждого животного есть уникальные характеристики, заданные в момент создания.
Сокращенная инициализация: параметры свойств
TypeScript предлагает изящный short-hand синтаксис для совмещения объявления свойств и их инициализации в конструкторе. Это позволяет избежать повторяющегося кода, особенно когда у класса много свойств.
Идея в том, чтобы добавить модификатор доступа (например, public, private, readonly) прямо к параметру конструктора. Компилятор TS автоматически создаст свойство класса с таким же именем и присвоит ему значение из параметра.
Перепишем наш класс с использованием этой возможности:
class Animal { // Заметь: мы больше не объявляем свойства здесь явно! // В параметрах конструктора добавляем модификаторы constructor( public name: string, public age: number, public species: string, public isMammal: boolean ) { // Тело конструктора теперь может быть пустым! // this.name = name; и т.д. происходит автоматически. console.log(`Новое животное ${this.name} создано!`); } }
Этот код делает ровно то же самое, что и предыдущий. Компилятор сам создаст свойства name, age, species и isMammal для класса Animal и проинициализирует их в конструкторе. Этот синтаксис чище и используется очень часто.
Важное примечание: Если ты не укажешь модификатор (public, private и т.д.), параметр name будет считаться просто локальной переменной конструктора, а не свойством класса. Поэтому модификатор обязателен для работы этого синтаксиса.
Методы класса: определяем поведение объекта
Если свойства описывают состояние объекта (какие данные он хранит), то методы описывают его поведение (что он может делать). Методы класса это просто функции, объявленные внутри класса и имеющие доступ к его свойствам и другим методам через this.
Синтаксис объявления метода очень похож на объявление функции, но без ключевого слова function.
Давай добавим нашему животному несколько осмысленных действий, методов.
class Animal { constructor( public name: string, public age: number, public species: string, public isMammal: boolean ) {} // Метод, который возвращает строку с представлением животного getDescription(): string { return `${this.name} - это ${this.species} ${this.isMammal ? 'млекопитающее' : 'не млекопитающее'}. Возраст: ${this.age} лет.`; } // Метод, который "заставляет" животное постареть на 1 год haveBirthday(): void { this.age++; // Увеличиваем возраст на 1 console.log(`С днем рождения, ${this.name}! Теперь тебе ${this.age}.`); } // Метод, который выводит звук животного (упрощенно) makeSound(sound: string = "неизвестный звук"): void { console.log(`${this.name} издает: ${sound}!`); } }
Теперь наши животные ожили! Они умеют представляться, праздновать день рождения и издавать звуки.
Давай посмотрим на них в действии:
let simba = new Animal("Симба", 5, "Лев", true); let quacky = new Animal("Кваки", 1, "Лягушка", false); // Вызываем методы у созданных объектов console.log(simba.getDescription()); // "Симба - это Лев млекопитающее. Возраст: 5 лет." quacky.haveBirthday(); // "С днем рождения, Кваки! Теперь тебе 2." console.log(quacky.age); // 2 - свойство действительно изменилось! simba.makeSound("Рррррр!"); // "Симба издает: Рррррр!" quacky.makeSound(); // "Кваки издает: неизвестный звук!" (использовано значение по умолчанию)
Обрати внимание, как внутри методов мы обращаемся к свойствам объекта: this.name, this.age. this гарантирует, что мы работаем с данными именно того экземпляра, у которого был вызван метод. Когда мы вызываем quacky.haveBirthday(), this внутри метода haveBirthday указывает на объект quacky.
Практические задачи для закрепления
Давай превратим её в знания, выполнив несколько задач.
Задача 1: Класс «Пользователь»
Создай класс User, который представляет пользователя в системе.
-
Свойства:
username(строка),email(строка),isOnline(булево, по умолчаниюfalse). -
Конструктор должен инициализировать все свойства, кроме
isOnline, которое должно быть установлено вfalseпри создании, если не передано иное. -
Метод
login(), который меняет статусisOnlineнаtrueи выводит сообщение в консоль${username} вошел в систему.. -
Метод
logout(), который меняет статусisOnlineнаfalseи выводит сообщение${username} вышел из системы.. -
Метод
getStatus(), который возвращает строку видаПользователь ${username} (${email}) сейчас ${isOnline ? 'online' : 'offline'}..
Решение:
class User { isOnline: boolean; constructor( public username: string, public email: string, isOnline: boolean = false // Параметр по умолчанию ) { this.isOnline = isOnline; } login(): void { this.isOnline = true; console.log(`${this.username} вошел в систему.`); } logout(): void { this.isOnline = false; console.log(`${this.username} вышел из системы.`); } getStatus(): string { const status = this.isOnline ? 'online' : 'offline'; return `Пользователь ${this.username} (${this.email}) сейчас ${status}.`; } } // Проверяем let user1 = new User("max_dev", "max@yandex.ru"); let user2 = new User("anna_coder", "anna@google.com", true); console.log(user1.getStatus()); // ... сейчас offline. user1.login(); // max_dev вошел в систему. console.log(user1.getStatus()); // ... сейчас online. console.log(user2.getStatus()); // ... сейчас online. user2.logout(); // anna_coder вышел из системы.
Задача 2: Класс «Прямоугольник»
Создай класс Rectangle.
-
Свойства:
width(число),height(число). -
Используй сокращенный синтаксис параметров свойств в конструкторе.
-
Реализуй метод
getArea(), который вычисляет и возвращает площадь прямоугольника. -
Реализуй метод
getPerimeter(), который вычисляет и возвращает периметр прямоугольника. -
Реализуй метод
scale(factor: number), который увеличивает ширину и высоту прямоугольника в указанное количество раз.
Решение:
class Rectangle { constructor( public width: number, public height: number ) {} getArea(): number { return this.width * this.height; } getPerimeter(): number { return 2 * (this.width + this.height); } scale(factor: number): void { this.width = this.width * factor; this.height = this.height * factor; } } // Проверяем let rect = new Rectangle(5, 10); console.log("Площадь:", rect.getArea()); // 50 console.log("Периметр:", rect.getPerimeter()); // 30 rect.scale(2); console.log("Новая ширина:", rect.width); // 10 console.log("Новая высота:", rect.height); // 20 console.log("Новая площадь:", rect.getArea()); // 200
Задача 3: Класс «Банковский счет» (посложнее)
Создай класс BankAccount.
-
Свойства:
accountNumber(строка, номер счета),balance(число, текущий баланс, по умолчанию 0),owner(строка, владелец счета). -
Реализуй метод
deposit(amount: number), который добавляет сумму к балансу и выводит сообщение о внесении. -
Реализуй метод
withdraw(amount: number), который вычитает сумму из баланса. Не позволяй снять больше средств, чем есть на счете. Выводи соответствующие сообщения об успешном снятии или ошибке. -
Реализуй метод
getBalance(), который возвращает текущий баланс.
Решение:
class BankAccount { balance: number = 0; constructor( public accountNumber: string, public owner: string, initialBalance: number = 0 ) { this.balance = initialBalance; } deposit(amount: number): void { if (amount <= 0) { console.log("Сумма для депозита должна быть положительной."); return; } this.balance += amount; console.log(`На счет ${this.accountNumber} внесено ${amount}. Новый баланс: ${this.balance}`); } withdraw(amount: number): void { if (amount <= 0) { console.log("Сумма для снятия должна быть положительной."); return; } if (amount > this.balance) { console.log(`Недостаточно средств на счете ${this.accountNumber}. Запрошено: ${amount}, доступно: ${this.balance}`); return; } this.balance -= amount; console.log(`Со счета ${this.accountNumber} снято ${amount}. Новый баланс: ${this.balance}`); } getBalance(): number { return this.balance; } } // Проверяем let myAccount = new BankAccount("DE91100000000123456789", "Максим", 100); myAccount.deposit(50); // Внесено 50. Новый баланс: 150 myAccount.withdraw(75); // Снято 75. Новый баланс: 75 myAccount.withdraw(100); // Недостаточно средств... Доступно: 75 console.log("Текущий баланс:", myAccount.getBalance()); // 75
Заключение по уроку
Сегодня мы с тобой прошли гигантский пласт материала. Ты научился:
-
Объявлять классы с помощью ключевого слова
class. -
Явно объявлять свойства класса с аннотациями типов.
-
Использовать конструктор для инициализации объектов. Это сердце класса, где задаются его начальные параметры.
-
Применять сокращенный синтаксис Parameter Properties (
public name: stringв конструкторе) для более лаконичного кода. -
Создавать методы, функции которые определяют поведение объектов и работают с их внутренним состоянием через
this.
В следующих уроках мы углубимся в более сложные концепции ООП в TypeScript: наследование, инкапсуляцию (модификаторы доступа private, protected) и полиморфизм.
Попробуй создать свои классы: Book, Car, Product. Чем больше практики, тем прочнее знания.
Если ты хочешь двигаться дальше и освоить TypeScript на все 100%, жду тебя на полном курсе, где мы разберем всё от А до Я.
Полный курс с уроками по TypeScript для начинающих: https://max-gabov.ru/typescript-dlya-nachinaushih
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


