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

Как работает механизм Private Fields (закрытых свойств) в классах TypeScript? Чем отличаются приватные свойства с использованием модификатора private и с символом #, в чем особенности их применения и ограничения?

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

Ответ.

История вопроса

В TypeScript приватные свойства появились изначально с помощью модификатора private, а начиная с ECMAScript 2021 в JavaScript поддерживаются настоящие закрытые поля c помощью символа #. Основная цель — инкапсуляция данных, чтобы защитить внутренние детали класса от доступа извне.

Проблема

Модификатор private в TypeScript обеспечивает приватность только на этапе компиляции, но в скомпилированном JavaScript эти поля остаются доступными через прямой доступ к свойствам объекта. Это может привести к непреднамеренному изменению состояния объекта. ES Private Fields (#) полностью недоступны вне класса даже на уровне рантайма JS, что обеспечивает реальную защиту данных.

Решение

TypeScript поддерживает оба подхода. При выборе между ними ориентируйтесь на нужную степень защиты и ограничения на совместимость с версиями JS.

Пример кода:

class Example { private hidden: number; #trulyHidden: string; constructor() { this.hidden = 42; this.#trulyHidden = 'secret'; } getHidden() { return this.hidden; } getTrulyHidden() { return this.#trulyHidden; } } const x = new Example(); // x.hidden — ошибка в TS, но сработает через x['hidden'] в JS // x.#trulyHidden — синтаксическая ошибка даже в JS

Ключевые особенности:

  • private гарантирует приватность только на этапе компиляции TypeScript
  • #field даёт настоящую приватность на уровне исполнения JavaScript
  • Использование # требует поддержки современного JS (ES2021+)

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

Можно ли получить доступ к private полю класса TypeScript через прямое обращение в JS?

Да, можно, так как приватность реализована только на этапе компиляции. В исходном JS поле будет обычным свойством объекта. Например:

class A { private x = 1; } const a = new A(); console.log((a as any)["x"]); // 1

Можно ли использовать приватные поля с # в интерфейсах или описывать их с помощью type?

Нет, #private поля — часть только реализации класса и не могут быть описаны в интерфейсах, типах или использованы вне самого класса. Интерфейсы описывают только публичные члены.

Можно ли наследовать приватные поля, объявленные с помощью #?

Нет, такие поля вообще не доступны классам-наследникам. Только сам класс имеет к ним доступ:

class Parent { #foo = 123; } class Child extends Parent { // this.#foo = 444; // Ошибка }

Типовые ошибки и анти-паттерны

  • Использование private для защиты критичных данных — приводит к уязвимости при прямом доступе
  • Путаница между приватностью на уровне типов и рантайма
  • Попытка использовать #fields в старых версиях JS

Пример из жизни

Негативный кейс

В проекте для хранения паролей в классе User использовано поле private password. Один из разработчиков случайно получает доступ через user['password'] для дебага, а поле оказывается изменённым сторонним модулем.

Плюсы:

  • Простая интеграция с TypeScript

Минусы:

  • Нет реальной защиты в рантайме JS
  • Легко обойти приватность нечаянно

Позитивный кейс

Разработчик использует ES private field #password. Теперь попытки обращения к полю через индексацию или в наследниках не проходят, безопасность данных обеспечена даже при использовании сторонних библиотек и инструментария.

Плюсы:

  • Настоящая приватность
  • Надёжная защита чувствительных свойств

Минусы:

  • Требует поддержки современного JS (не совместимо с ES5)
  • Приватные поля не видны в интерфейсах, нельзя переопределять в наследниках