ПрограммированиеFrontend разработчик

Как работают типы модификаторы доступа (public, private, protected) в TypeScript? Какие есть нюансы при использовании их с наследованием и при трансляции кода в JavaScript?

Проходите собеседования с ИИ помощником Hintsage

Ответ

В TypeScript модификаторы доступа используются для ограничения области видимости свойств и методов в классах:

  • public — свойство или метод доступны везде (по умолчанию).
  • private — доступны только в том классе, где объявлены.
  • protected — доступны в классе и его наследниках.
class Animal { public name: string; private age: number; protected kind: string; constructor(name: string, age: number, kind: string) { this.name = name; this.age = age; this.kind = kind; } } class Dog extends Animal { bark() { console.log(this.kind); // OK: protected // console.log(this.age); // Ошибка: private не видно в наследнике } } const dog = new Dog('Шарик', 5, 'млекопитающее'); console.log(dog.name); // OK // console.log(dog.kind); // Ошибка: protected // console.log(dog.age); // Ошибка: private

Нюансы:

  • В скомпилированном JavaScript модификаторы private и protected реализуются только на этапе компиляции, физической изоляции в рантайме нет! Код на JS будет содержать все свойства (их, например, можно получить через обход объекта).
  • В новых версиях (с TypeScript 3.8) внедрён синтаксис для настоящих приватных полей: #field, но это уже спецификация JS, и компилируется иначе.

Вопрос с подвохом

Вопрос: Можно ли получить доступ к private свойству класса TypeScript после компиляции в JavaScript?

Ответ: Да, так как private (и protected) — это проверка TypeScript на этапе компиляции, после компиляции в ES5 или ES6 приватность не сохраняется, свойства остаются в объекте и доступны через обращение по имени свойства (например, через object['privateProp']).

// JS код после компиляции function Animal(name, age, kind) { this.name = name; this.age = age; this.kind = kind; } var dog = new Animal('Шарик', 5, 'млекопитающее'); console.log(dog['age']); // 5 — доступа нет только на уровне TS!

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В крупном проекте разработчик рассчитывал на факт недоступности private-полей в рантайме. В результате при передачи объекта сериализацией (JSON.stringify) в логи случайно попали конфиденциальные данные, так как типизация TS не защитила от реального доступа к полям.


История

В проекте был реализован механизм "расширения" экземпляров классов через динамическое добавление свойств. Динамически добавленные свойства перезаписывали private-названия, и их случайно модифицировали внешний код. Ошибка была обнаружена только на production, причем приватность не была обеспечена.


История

При миграции с JavaScript на TypeScript команда стала использовать protected, считая, что это защищает от использования полей вне дочерних классов. Но программист через Object.assign по ошибке перезаписал protected-поле в рантайме и получил трудноотлавливаемый баг. Это стало возможным, потому что runtime-инкапсуляции не было.