Классы
Члены класса и аксессоры, модификаторы доступа, свойства-параметры, наследование и абстрактные классы.
12 вопросов
JuniorТеорияОчень частоЧто делают public, private и protected в классе?
Что делают public, private и protected в классе?
Они управляют видимостью членов. public — по умолчанию — доступен везде. private доступен только внутри объявляющего класса. protected доступен внутри класса и его производных классов. Эти модификаторы проверяются лишь на этапе компиляции и стираются из итогового JavaScript, поэтому для настоящей приватности в рантайме нужно использовать #private-поле.
Типичные ошибки
- ✗Считать, что
privateскрывает член в рантайме — это только проверка компиляции, она стирается - ✗Путать
privateиprotected(доступ из производного класса — этоprotected) - ✗Забывать, что
publicподразумевается по умолчанию, если модификатор не указан
Уточняющие вопросы
- →Чем
#private-поле отличается от модификатораprivate? - →Может ли производный класс читать
protected-член своего базового класса?
JuniorТеорияОчень частоКакие возможности членов класса поддерживают классы в TypeScript?
Какие возможности членов класса поддерживают классы в TypeScript?
Класс поддерживает типизированные поля экземпляра, constructor, методы, readonly-поля, задаваемые только в объявлении или конструкторе, static-члены, принадлежащие самому классу, а не экземплярам, и get/set-аксессоры, управляющие чтением и записью свойства. Поверх классов ES2015 TypeScript добавляет типы полей, модификаторы доступа и abstract.
Типичные ошибки
- ✗Думать, что к
static-членам обращаются через экземпляр, а не через класс - ✗Считать, что
readonlyможно присваивать где угодно, а не только в объявлении или конструкторе - ✗Полагать, что в TypeScript нет class-выражений или итераторов (это надмножество JS)
Уточняющие вопросы
- →В чём разница между членом экземпляра и
static-членом? - →Когда стоит использовать
get/set-аксессор вместо обычного поля?
MiddleТеорияОчень частоКак работает наследование классов в TypeScript?
Как работает наследование классов в TypeScript?
class B extends A заставляет B наследовать члены A. Конструктор производного класса обязан вызвать super(...) до обращения к this. Методы можно переопределять, при желании помечая их override, а super.method() вызывает базовую версию. protected-член доступен в производных классах, а private — нет. TypeScript допускает только одиночное наследование — один базовый класс, — поэтому для нескольких контрактов используйте интерфейсы.
Типичные ошибки
- ✗Использовать
thisв конструкторе производного класса до вызоваsuper(...) - ✗Считать, что
private-члены наследуются и доступны в производном классе (доступны толькоprotected) - ✗Ожидать множественное наследование классов — TypeScript допускает только один базовый класс
Уточняющие вопросы
- →Что добавляет ключевое слово
overrideсверх обычного переопределения метода? - →Как дать классу несколько контрактов без множественного наследования?
JuniorТеорияЧастоЧто такое аксессоры get и set в классе TypeScript?
Что такое аксессоры get и set в классе TypeScript?
Аксессоры перехватывают чтение и запись свойства телами методов, при этом вызывающие пользуются обычным синтаксисом свойства. get name() выполняется при чтении свойства и возвращает значение; set name(v) выполняется при присваивании и получает новое значение. Они дают возможность добавить проверку, вычисление или логирование за obj.name. Свойство только с геттером фактически доступно лишь для чтения.
Типичные ошибки
- ✗Вызывать аксессор со скобками —
obj.name, а неobj.name() - ✗Путать, какое ключевое слово срабатывает при чтении и записи (
getчитает,setпишет) - ✗Забывать, что геттер без сеттера делает свойство доступным только для чтения
Уточняющие вопросы
- →Как сделать вычисляемое свойство без поля-хранилища под ним?
- →Что произойдёт, если задать геттер без парного сеттера?
JuniorТеорияЧастоЧто делает модификатор readonly у свойства?
Что делает модификатор readonly у свойства?
readonly помечает свойство как присваиваемое лишь один раз — при объявлении или внутри конструктора — после чего компилятор отвергает любое переприсваивание. Это только проверка на этапе компиляции, она стирается из итогового JavaScript, поэтому неизменяемости в рантайме не даёт. Он закрепляет привязку, а не содержимое: readonly-массив всё ещё можно изменить через push.
Типичные ошибки
- ✗Ожидать, что
readonlyзапретит изменение в рантайме — он стирается после компиляции - ✗Думать, что элементы
readonly-массива нельзя менять (закреплена лишь привязка) - ✗Считать, что
readonly-поле нельзя установить вовсе, а не один раз в конструкторе
Уточняющие вопросы
- →Как служебный тип
Readonly<T>связан с модификаторомreadonly? - →Почему на
readonly-свойстве-массиве всё ещё можно вызватьpush?
JuniorТеорияЧастоЧем static-член класса отличается от члена экземпляра?
Чем static-член класса отличается от члена экземпляра?
static-член принадлежит самому классу: он существует в единственном числе и доступен как ClassName.member, а не через экземпляр. Член экземпляра создаётся для каждого объекта при new и доступен через this. Внутри static-метода this — это класс.
Типичные ошибки
- ✗Обращаться к
static-члену через экземпляр, а не через класс - ✗Думать, что у каждого экземпляра своя копия
static-члена - ✗Ожидать, что
thisвнутриstatic-метода указывает на экземпляр
Уточняющие вопросы
- →Как обратиться к
static-члену из производного класса, расширяющего этот класс? - →Когда вычисляется инициализатор
static-поля?
MiddleТеорияЧастоЧто такое абстрактный класс и когда его использовать?
Что такое абстрактный класс и когда его использовать?
Абстрактный класс — это базовый чертёж, который нельзя инстанцировать напрямую. Его abstract-методы объявляют сигнатуру без тела, которую обязан реализовать каждый производный класс, но он также может содержать конкретные методы, поля и конструктор, общий для производных классов. Используйте его, чтобы разделить реализацию, заставляя производные классы дополнить детали. В отличие от интерфейса, абстрактный класс несёт реализацию и состояние.
Типичные ошибки
- ✗Пытаться инстанцировать абстрактный класс напрямую через
new - ✗Думать, что
abstract-метод может иметь тело (он содержит только сигнатуру) - ✗Путать его с интерфейсом — абстрактный класс может нести реализацию и состояние
Уточняющие вопросы
- →Когда выбрать абстрактный класс вместо интерфейса?
- →Что произойдёт, если производный класс не реализует
abstract-метод?
MiddleТеорияЧастоЧем #private-поле отличается от модификатора private?
Чем #private-поле отличается от модификатора private?
private — аннотация TypeScript: проверяющий её соблюдает, а затем стирает, поэтому свойство остаётся в итоговом JavaScript и доступно через obj['x']. #x — поле ECMAScript, которое обеспечивает движок: код вне класса не может его назвать и не увидит в Object.keys.
Типичные ошибки
- ✗Считать, что
privateскрывает свойство в рантайме — оно стирается и читается приведением типа - ✗Полагать, что
#xкомпилируется в обычное свойствоx, доступное коду снаружи - ✗Ожидать, что
#private-поле появится в выводеObject.keysилиJSON.stringify
Уточняющие вопросы
- →Как проверить, что объект несёт заданное
#private-поле? - →Почему производный класс не может прочитать
#private-поле своего базового класса?
JuniorТеорияИногдаЧто такое свойства-параметры в конструкторе TypeScript?
Что такое свойства-параметры в конструкторе TypeScript?
Свойство-параметр — это параметр конструктора с модификатором доступа (public, private, protected) и/или readonly. TypeScript тогда автоматически объявляет поле класса с тем же именем и присваивает ему аргумент, поэтому constructor(private name: string) {} заменяет отдельное объявление поля плюс this.name = name. Это сокращение, убирающее шаблонный код.
Типичные ошибки
- ✗Думать, что обычный типизированный параметр становится полем — нужен модификатор или
readonly - ✗Забывать, что свойства-параметры работают только в
constructor, а не в других методах - ✗Всё равно писать избыточное присваивание
this.name = nameрядом с модификатором
Уточняющие вопросы
- →Что будет, если не указать модификатор у параметра конструктора?
- →Можно ли совместить
readonlyсprivateна одном свойстве-параметре?
MiddleТеорияИногдаЧто даёт private readonly сверх обычного readonly-поля?
Что даёт private readonly сверх обычного readonly-поля?
Видимость. Модификаторы ортогональны: readonly управляет изменяемостью, private — видимостью. Обычное readonly-поле присваивается один раз, но читается снаружи; private readonly скрывает его и снаружи класса. Обе проверки — на этапе компиляции, они стираются при сборке.
Типичные ошибки
- ✗Думать, что
readonlyещё и скрывает поле за пределами класса - ✗Считать, что
privateослабляетreadonlyвнутри объявляющего класса - ✗Ожидать, что любой из модификаторов доживёт до итогового JavaScript
Уточняющие вопросы
- →Где
readonly-полю разрешено присваивание? - →Как открыть
private readonly-поле вызывающим, не давая им запись?
MiddleТеорияИногдаНасколько глубока неизменяемость, которую readonly даёт свойству?
Насколько глубока неизменяемость, которую readonly даёт свойству?
На один уровень. readonly закрепляет привязку — свойство нельзя переприсвоить, — но не трогает объект, на который оно указывает, поэтому readonly items: string[] всё ещё допускает items.push(x). Readonly<T> так же поверхностен; настоящая глубина требует рекурсивного типа.
Типичные ошибки
- ✗Ожидать, что
readonlyзапретитpushна свойстве-массиве - ✗Считать
Readonly<T>глубоким, а не отображающим один уровень ключей - ✗Путать проверку на этапе компиляции с рантайм-заморозкой
Object.freeze
Уточняющие вопросы
- →Чем
readonly string[]отличается отreadonly items: string[]? - →Как выразить глубоко неизменяемый тип для вложенного объекта?
SeniorТеорияИногдаЧто подавляет утверждение определённого присваивания в name!: string?
Что подавляет утверждение определённого присваивания в name!: string?
Он подавляет strictPropertyInitialization — проверку того, что поле без ? присвоено в объявлении или в конструкторе. ! не добавляет ни проверки в рантайме, ни инициализации, поэтому в поле вполне может лежать undefined, хотя тип обещает string.
Типичные ошибки
- ✗Думать, что
!заставляет компилятор выпустить рантайм-проверку или инициализатор по умолчанию - ✗Считать, что поле с
!никогда не окажетсяundefinedв рантайме - ✗Хвататься за
!вместо того, чтобы инициализировать поле или сделать его опциональным
Уточняющие вопросы
- →Какой флаг компилятора включает
strictPropertyInitialization? - →Какие есть более безопасные варианты для поля, присваиваемого вне конструктора?