Урок по треку TypeScript 6 мин

Классы и интерфейсы в TypeScript

Модификаторы доступа, abstract, implements, структурная типизация.

Классы в TypeScript это классы JavaScript плюс проверки при компиляции. Важно понимать, какие проверки исчезают в рантайме, а какие нет.

Поля и модификаторы доступа

class User {
  public name: string;          // public по умолчанию
  protected role = 'user';      // в классе и наследниках
  private token?: string;       // только внутри класса, проверка компилятора
  #secret = 42;                 // настоящее приватное поле JS
  readonly id: number;

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

private существует только в типах: в рантайме поле обычное, и (user as any).token его прочитает. #secret приватно на уровне языка: снаружи до него не добраться даже через any.

readonly запрещает присваивание вне конструктора, но тоже только при компиляции.

Параметры-свойства

class Point {
  constructor(public x: number, private readonly y: number) {}
}

Модификатор в параметре конструктора объявляет и заполняет поле. Это синтаксис TypeScript, он не работает в режиме erasableSyntaxOnly (TS 5.8) и при запуске .ts через Node.js с отсечением типов, потому что требует генерации кода.

implements и extends

interface Serializable { toJSON(): string }

class Order implements Serializable {
  toJSON() { return '{}'; }
}

implements только проверяет, что класс соответствует интерфейсу. Типы параметров методов из интерфейса не переносятся в класс, их нужно писать.

extends наследует реализацию. Класс наследует один класс, но реализует сколько угодно интерфейсов. Интерфейс может расширять несколько интерфейсов и даже класс.

Абстрактные классы

abstract class Shape {
  abstract area(): number;
  describe() { return `Площадь ${this.area()}`; }
}
class Circle extends Shape {
  constructor(private r: number) { super(); }
  area() { return Math.PI * this.r ** 2; }
}
new Shape(); // ошибка

В отличие от интерфейса, абстрактный класс существует в рантайме и может содержать готовую реализацию и состояние.

override

С флагом noImplicitOverride метод, переопределяющий родительский, обязан иметь override. Если метод в родителе переименуют, компилятор сразу покажет, что переопределять нечего.

Структурная совместимость классов

Классы сравниваются по форме, как интерфейсы:

class A { x = 1 }
class B { x = 2 }
const a: A = new B(); // ок

Исключение: если в классе есть private или protected поле, совместимы только экземпляры этого класса и его наследников.

Типы для самого класса

typeof User это тип конструктора, User в позиции типа это тип экземпляра. Для фабрик пишут new (...args: any[]) => T.

Частые ошибки

  • Думать, что private защищает данные в рантайме.
  • Ждать вывода типов параметров из implements.
  • Путать abstract class и interface: первый попадает в бандл.
  • Передавать метод как колбэк и терять this. Помогают стрелочные поля или bind.

Что спросят на собесе

  • Разница private и #private.
  • Абстрактный класс против интерфейса: когда что.
  • Что проверяет implements.
  • Что такое параметры-свойства конструктора.
  • Почему два разных класса с одинаковыми полями совместимы.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.