Классы и ООП
Классы и ООП
Технический фундамент классов в TypeScript
TypeScript усиливает классы типовыми ограничениями:
- поля и методы получают явные контракты;
- модификаторы доступа фиксируют границы API класса;
readonlyзащищает инварианты от случайной мутации;- наследование проверяется на совместимость сигнатур.
Практически это превращает класс из "контейнера логики" в строго описанный модуль поведения с контролируемым доступом.
Что добавляет TypeScript к классам
JavaScript уже поддерживает классы, но TypeScript делает их типобезопаснее и выразительнее: типы полей и методов, модификаторы доступа, readonly, абстрактные классы. Это помогает строить ООП-модели с четкими контрактами.
Ключевой момент: TypeScript в классах не меняет ООП-идею, но добавляет строгие правила, которые предотвращают ошибки на этапе разработки.
Проверь себя: почему private и readonly полезны в больших командах, где один объект может использоваться в десятках мест?
Типы полей и методов
class User {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getLabel(): string {
return `${this.name} (${this.age})`;
}
}
Смотри, что важно: класс сразу показывает, какие данные хранит и какие методы предоставляет.
Модификаторы доступа
TypeScript поддерживает:
public- доступно везде (по умолчанию);private- доступно только внутри класса;protected- внутри класса и наследников.
class Account {
private balance: number = 0;
deposit(amount: number): void {
this.balance += amount;
}
getBalance(): number {
return this.balance;
}
}
Здесь часто путаются: private не дает напрямую изменить balance снаружи, и это защищает инварианты класса.
private в TypeScript и #private в JavaScript
Важно: модификаторы доступа private/protected в TypeScript работают на этапе компиляции. После компиляции в JavaScript это обычные свойства.
Если нужна реальная приватность в рантайме, в современном JavaScript есть #private-поля.
class A {
private x = 1; // TS-приватность: проверяется компилятором
}
const a = new A();
// (a as any).x; // в рантайме свойство существует, но TS запрещает доступ без "обхода"
class B {
#x = 1; // JS-приватность: поле недоступно снаружи даже в рантайме
getX() {
return this.#x;
}
}
const b = new B();
// b.#x; // Ошибка: приватное поле недоступно
Упрощенный синтаксис конструктора
class Product {
constructor(
public id: string,
public title: string,
private cost: number
) {}
getCost(): number {
return this.cost;
}
}
Параметры с модификаторами автоматически становятся полями класса.
implements: класс как реализация контракта
Интерфейс можно использовать как контракт, который класс обязан реализовать.
interface Greeter {
greet(): string;
}
class UserGreeter implements Greeter {
constructor(public name: string) {}
greet(): string {
return `Hello, ${this.name}`;
}
}
Смотри, что важно: implements проверяет, что класс соответствует контракту, но сам интерфейс не существует в рантайме.
Наследование и переопределение
class Person {
constructor(public name: string) {}
greet(): string {
return `Hello, ${this.name}`;
}
}
class Admin extends Person {
greet(): string {
return `Admin: ${this.name}`;
}
}
TypeScript проверяет совместимость сигнатур при переопределении.
Проверь себя: что произойдет, если в наследнике изменить тип возвращаемого значения метода на несовместимый?
Абстрактные классы
Абстрактный класс задает общий контракт, но не предназначен для прямого создания экземпляров.
abstract class Payment {
abstract pay(amount: number): boolean;
}
class CardPayment extends Payment {
pay(amount: number): boolean {
return amount > 0;
}
}
Это удобно, когда есть общая модель поведения с разными реализациями.
Реальные микро-сценарии
- Модель пользователя и ролей.
private защищает внутренние данные, public дает контролируемый API.
- Платежные провайдеры.
Абстрактный класс задает обязательный метод pay.
- Каталог товаров.
readonly id гарантирует неизменность ключа сущности.
Частые ошибки новичков
- Делать все поля
publicпо умолчанию. - Использовать классы там, где достаточно простых функций и типов.
- Перегружать классы множеством обязанностей.
- Пытаться обойти типизацию через
as any.
Анти-провал: у каждого класса должна быть одна понятная ответственность и явный публичный контракт.
Что будет, если изменить входные данные
Если конструктор ожидает number, а получает строку, TypeScript остановит это до запуска. Если поле объявлено private, внешняя попытка изменить его даст ошибку компиляции. Это снижает число неявных мутаций и случайных побочных эффектов.
Проверь себя: где лучше контролировать изменение критичного поля - через public доступ или через методы класса?
Краткий итог
- TypeScript делает ООП-код строже и безопаснее.
- Типы полей, методов и модификаторы доступа задают четкие границы ответственности.
private/protectedпомогают защищать внутреннее состояние.- Абстрактные классы полезны для общих контрактов с разными реализациями.
- Классы в TS эффективны, когда отражают реальную модель домена, а не используются "по привычке".