Галоўная / Артыкулы / Прыватныя поля TypeScript проты сінтаксісу `#`: прыватнасць падчас компіляцыі чы рэалізаваная падчас виконання?

Прыватныя поля TypeScript проты сінтаксісу `#`: прыватнасць падчас компіляцыі чы рэалізаваная падчас виконання?

Порушаець модифікатор `private` у TypeScript, який діє падчас компіляцыі, з полямі `#` у ECMAScript, якіе рэалізуюцца падчас виконання, каб дапамогчы выбраць наяўны стратэгію інкапсуляцыі для кодавых баз 2026 года.

2729 слоў

Большая частка проблем, зв’язаных з прытнасцю, у проектах на TypeScript, выклікаецца памылковым адначытаннем двух модэляў інкапсуляцыі, якія на практыцы не працуюць аднакова: ключавага слова private, якое дзейсніцца толькі на рэверсі кампайляра, і синтаксы поля # з ECMAScript, якая рэалізуецца пад час выканання. Команды часта выбіраюць адзін падход без глыбокага разважэння, запускаюць проект і пазней сталквуюцца з ситуацыямі, калі гэты выбор паслужае прычыной неспадзяваных проблем.

Ключава слова private у TypeScript не забезпечвае ніякай захопненасці пасля таго, як код запускаецца. Кампайляр пераканальваеся на видныцю элементаў пад час напісання та складання проекта, але JavaScript, які ён выдае, ператварае кожны „прытнасцевы“ элемент у звычную публічную властывасць. Будзь-што, што выкарыстоўвае скомпільаваны выхід, можа працягнуць свой доступ за межы модифікатора прытнасці.

Поле ECMAScript # выкорыстоўвае іншы падход, які забезпечвае абоўсюдную прыватнась. Сама среда выканання адстаўляе граніцы, зберагаючы даны поля ў внутранім WeakMap, таму зовнішні код не можа да яго дасягнуць. Гэта гарантія захоўвае вас ад нешчырага выкарыстоўвання і робіць чутлівыя значэнні безпечнымі нават у средах, якім вы не абліковыяце на 100%.

Выбор між гэтымі двума падходамі вялічы, чы реальна будзе адбывацца інкапсуляцыя, калі код прайдзе у працэйную среду, таму так важна правільна ўсё выконаць.

Ключовыя выводы

  • Модифікатор private у TypeScript стыраецца пад час компіляцыі, залишаючы звычныя, вольна доступныя атрыбуты JavaScript. На протывесу гэму, поля ECMAScript # выкорыстоўваюць збераганне на базе WeakMap, якое захоўвае прыватнась нават пасля транспіляцыі.
  • Выберыце private, калі вам патрэбна безпека на рывень типаў у кодбазе, дзе TypeScript ёсць едыным корыстнікам, і дапамагаюць толькі перакладчык у час компілявання. Выберыце #, калі публікуеце бібліятэку, працуеце з дынамічнымі імпортамі або хочаце захаваць чутлівыя значэння ад перагляду ў час выканання.
  • Этыя два механізмы не ўзаемна заменяюць адзін другога. Змена класу з private на # праглытае змены ў відкрытым API і можа нашкодзіць інструментам, якія выкарыстоўваюць рэфлексію. Без задокументаванай прымовы кодбазы вынужданыя сумешваць оба падходы нэўнасупершано.
  • Багатыя команды выбіраюць private проста таму, што гэта супадае з патернамі ў такіх мовах, як Java, а потым сталкаюцца з проблемамі, калі корыстнікі, якія выкарыстоўваюць толькі JavaScript, абсалютна ігнаруюць гэтыя правілы. Раследаванне такіх багоў часта ёсць складным і дорогім.
  • Пачаткам 2026 года TypeScript 5.7 і новэйшыя версіі падтрымляюць оба механізма з абсалютным выведэнням типаў. Выбір между ямі насправды залежыць ад довер'я: чы вы контроліруеце кожны фрагмент коду, який вплывае на ваш клас, чы можа ён запрацаваць у небяспечным сераўе?
  • Розумэнне модифікатора private у TypeScript: толькі час компіляцыі

    Ключавая слова private у TypeScript — гэта чыста структура типовой системы. Яна блакуе неуправомочаны доступ пад час разработкі, але пасля компіляцыі ствароўваны JavaScript мае звычныя, без захаванняў, атрыбуты. На практыцы функціі з модифікаторам private служыць больш для дакументацыі, чым як справжняя межа безпекі.

    Калі клас з атрыбутам private компілюецца у звычны JavaScript, модифікатор цэтага атрыбута зьнікае пачалі, і такі атрыбут стае звычным элементам класу, які будь-хто можа чытаць або зменяць без жадных праблем. Гэта стварае сэрьёзныя проблэмы у трох ситуаціях: падачы пакетаў у npm, дынамічным імпорце модуляў трэціх сторон або інтеграцыі з інструментамі, якія выкарыстоўваюць рэфлексію, такімі як серыязавальнікі і ORM-ы.

    Простатаецтва — галоўная перавага атрыбута private. Разработчыкі, якія працавалі з такімі мовамі, як Java чысто C#, адразу ж зрозумеваюць яго значэнне. Редактары скрываюць прыватныя элементы класу ад прапаноў автодаполнення, інструменты рефакторавання выраховваюць правільную видазрымасць такіх элементаў, а компілятор паведамляе пра випадковую ўразрымасць пад час разработкі чысто перагляду коду.

    Проблемы пачываюць, калі выкладкі ўжо пра час выканання выявляюцца некоректнымі. Уявіце команду, якая стварае внутрэшні панель керування, выраховываючы, што кожны користувальнік іх коду выкарыстоўвае TypeScript. Чырэз калькі месца служба, напісаная на Python, завантажвае скомпіляваны пакет JavaScript і начынае безпасэродзейна мяніць токены сесій. Уявныя межы прыватнасці ніколі не існавалі паza межамі кампайляра, таму яны не стваралі жадных пераканаў.

    Этот модэль працуе добра, як толькі вы контролюеце весь ланцуг залежнасцяў і TypeScript застосоўваецца ад пачатку да канца. Як толькі ваш код пераходзіць межу мовы або публікуецца дзе-небудзь у публічным месцы, private перестае быць гарантіяй і стае простаю рэкамендацыяй.

    Прыватныя поля ECMAScript (#): Жорсткая прыватнасць, захаваная часам выканання

    Поле, якое пачатваецца з #, — это вяліканская функцыя JavaScript, якая стварае атрыбуты, якія сапраўды недаступны зза меж класу. Энжынер пашчывае іх у внутранім WeakMap, які захаваны ад рэфлексіі і ад будзь-якога звучнага коду, які прагчыць да іх дасягнуць. Калі вы настоўляеце ES2022 або новейшую версію, TypeScript захавае синтаксіс # у своем выходным кодзе.

    Пад час кампілявання для сучасных версій выданы JavaScript захавае синтаксіс # без змян, у працоўны час гарантуецца іх інкапсуляцыя: прабаўка дасягнуць поля на кшталт wallet.#balance зза меж класу пад строгім режымам выклікае адказ пра синтаксічную памялку. Нават вызов Object.keys(wallet) вяртае порожній масэвы, адтолькі што прыватныя поля зусім не ўключаюцца у стандартны механізм перыябраўвання атрыбутаў.

    Компрасам з полямі # являецца сумяшнасць. Калі неабходна падтрымка старэйшых средав, такіх як ES5 чыста ES2015, TypeScript вынушаны выдаваць поліфілы на адвароте WeakMap, якія збільшуюць вагу пакета і дадаюць дапамогу за кожны выход на данні — гэта справжня проблема для бібліятак, якія прагнуць эфектыўнае працаванне ў браузерах.

    Таксама існуе паследкі для досвяду разработчыка. Редактары не можаць адказваць на автодаполненне для полей # за межамі іх класаў, а дзеякіі інструменты для дэбаггінгу за замовчанням адкрываюць іх не для перагляду ў інспектары об’ектаў. Утрамбоўваючыя устаткі, такія як JSON.stringify, таксама мовчка ігнаруюць прыватныя поля, што можа здзівіць разработчыкаў, якія спакульваюць абоўсумовы апіс стану об’екта.

    Прыватныя поля маюць наібольшы сэнс у трох випадках: захаванне крыптаграфічных ключоў або токенав доступу, запобежанне спожывачам API ад парадкавання внутрашніх стабільных элементаў, а таксама запуск коду ў средах, дзе нельга цалкам паверыць у тое, што ўзмацнівае яго запуск. У такіх сцэнарыях строгая гарантія часу выконання мае большую цэннасць, чым зручнасць, яку вы пасвячаеце.

    Порэванне: калі кожны падход ўзяў перавагу

    Выбір между private і # ў канцы канцоў залежыць ад вашых граніцаў паверы і потрэб у інструментах — ні адна з гэтых опцыяў не є правильным стандартам у кожной ситуацыі. Гэта значыць, што команды павінны дагадацца пра часткавыя правілы, а не проста выбіраць тое, што здаецца найзнайомейшым.

    Вжывайце private у TypeScript, калі:

    • Вы ствараеце внутршняе застосунак, дзе кожны спожывач — это код на TypeScript пад строгімі настройкамі кампайляра.
  • Вы выкарыстоўваете ORM-ы, серыялізаторы або іншыя інструменты, якія базуюцца на рэфлексіі і патрабуюць перыябраць атрыбуты, каб стварыць схемы базы дадзеных або пакеты даных для API.
  • Вы компілюеце код для старэйшых версыйях ўжытку, такіх як ES5, дзе викорыстанне поліфіліў для поль # збільшыла бы размах пакета занадто сильна.
  • Для такіх класаў досвід разработчыка і функцыя автодаполнення маюць большое значэнне, чым безпека на рэвэлі ўжытку.
  • Выкарыстоўвайце поль ECMAScript #, калі:

    • Вы публікуеце пакет у npm і не можете гарантаваць, што кожны корыстувач будзе дазвольваць толькі стандарты TypeScript.
    • Вы зберагаеце чутлівыя даны, такія як токены абмовлення, ключы шифравання або інфармацыю платачэння, якія не должны быць доступныя для перагляду.
    • Вы ствараеце систему плагінаў, дзе недазволены код трэціх сторон выкарыстоўваецца разам з вашым кодам на той самы рэвэл ужытку.
  • Вы разрабатываеце фреймворк або SDK, дзе кантракт API павінен быць прымусова апляваны структурна, а не толькі задокументаваны.
  • Конфлікты выступаюць, калі дизайну трэба адночасна і падтрымка рэфлексіі, і справжня прыватнасць пад час выканання. Тыповы прыклад: ORM прагне перэйсчыць усі поля, каб створыць картоўку базы дадзенаў, але поля з # проста не праказуюцца ў гэтым перэйсчыце. Ёжыць гэта зазвычай значыць дадаць явныя методы-гетеры або дэкоратары метадаў, што прыносіць дапэўнюю складнасць, якой багато команд волілі бы ухіліцца.

    Тэставанне прыносіць ўзнёмкі. За дапамогою модифікатора private у TypeScript файлы тэстаў у там жа проекте все ўсё можу даць рэчы да внутранняга стану праз асерціі типаў. За дапамогою полей # зазвычай трэба выяўляць поведанне, якое можна тэставаць, у окремыя методы або ж спакойвацца на ін’єкцыі залежнасцяў. Команды, якія раней працавалі з внутранняй структурой коду пад час тэставання, часта вважаюць такі дадатковыя труднасці раздражаючымі.

    Якща паглядзець на ситуацыю ў 2026 годзе, поля # стаюць все болей популярнымі ў бібліятэках, чутлівых да безпекі, тады як модифікатор private застаецца стандартам для внутранняга коду прыкладнаў. TypeScript 5.7 спрыяе як аднаму, так і другаму падходу як да функцый першага класу, з можлівасцю інферэнсаў та пераканання ў наявнасці бягунковых ситуацый, таму рашэнне насправдзе залежыць ад архітэктуры, а не ад можлівасцяў кампайляра.

    Код у рэальных умовах: адпрацоўка обох патэранаў

    Шырока практыка — калі ёсць адзін кодавая база для вырабоцтва, яна можа адночасна викорыстоўваць оба шаблону для разных цэлаў. Галоўнае — паставацься адносова да аднакоўных меж: вярці private для звычайных деталяў рэалізацыі, а # заставіць для полей, якія стосуюцца безпекі.

    Разглядзім клас, які зберагае поле requestCache, пазначанае як private, а таксама поле #authToken, пазначанае як hard-private. Поле requestCache застаецца private, таму што інструменты тэставання і адлагоджвання можаць выкарыстоўваць яго, а інструменты серыялізацыі могу яго запісаць, як тое будзе патрэбна. Тым часам #authToken мае высокі рэжым прыватнасці, адтакулькі яго адкрыце пад час выканання могла б стварыць рэальную абавесу ў безпеці.

    Спалюванне гэтых двух элементаў мае сенс тады, калі поля несу разныя рывкі рызыку. Такія элементы, як значэння налаштаванняў і кешы, ёсць внутршнія деталі, дзе певная гнучкасць ўсё ж прыемна. З іншай боку, аутентифікацыйныя даны і крыптаграфічныя ключы патрабуюць болей строгай гарантіі працы праграмы.

    Іншы практычны прыклад — машына станоў, якая зберагае сваія правіла пераходу ў статусе private, але хавае свой фактычны стан за #. Машына станоў выклікае метод getState(), каб адкрыць стан, тады як поле #currentState залишаецца недаступным, чыгунам не дазволяючы яму безпасабліва зменіць стан. Карточка validTransitions застаецца полем private, таму што наборы тэстаў можа ўсё ж патрабаваць перагляду правіл, каб пераканацца ў правільнасці рэшэнняя крайніх кейсаў.

    Гэты стандарт добра працуе ў разных масштабах. Кодбаза з, скажамо, 50 класаў можа выкарыстоваць поля # у адносна 10 класах, звязаных з аутэнтыкацыяй, тады калі ў всіх іншых класах викорыстоўваецца модифікатор private. Няверагоднае выражэнне # у кодзе служыць чыстым сігналам пра тое, што вы прайшлі за межы сектара, чутлівага да безпекі.

    Стратэгіі міграцыі і стандарты команды

    Пераклад класа з модифікатора private на # ўплывае на яго публічны аспект, таму ён лямець змян. Будзь-якіі інструменты, якіе завісяць ад перэйранняя ўласнасцей класа, перестануць правільна працаваць, таму такая міграцыя павінна быць спланаваная адзіночна і адбывацца паэтапна, з коордынацыяй усіх команд, якія выкарыстоўваюць афектаваны код.

    Міграцыя класа з модифікатора private на поля # зазвычай адбываецца па прыемлімай сэрый:

    1. Перагляньце, які поля насправды выкалеквалізуюць патрэбу ў прыватнасці, якая трэба адбавляць пад час выканання, і якія поля патрабуюць толькі пераканання ў видимасці на рэвэлі компайляра.
    2. Адкладзіце выпуск новай версіі, якая дакументуе змены ў публічным API.
    3. Пераканце поля, критычныя для безпекі, у синтаксі # у спецыяльнай галузі фічар.
    4. Перапрацавайце внутрашнія тэсты так, каб яны больш не залежалі ад прымусовага доступу да полей.
    5. Пяраконайцеся, што бібліятэкі серыялізаціі і ORM продовжваюць правільна працаваць з аднаведзеным распадам полей.
    6. Апублікуйце выпуск з дакладнымі прымечаннямі, якія чытальна адказваюць на пытанне, што самэўжо зламалася і чаму.

    У внутрэшнях кодавых базах ёсць простыяй шлях для такіх змян. Паколькі няма зовнішняго корыстувальця, пра якога трэба думаць, каманды можу поступова впрысквати змяны, не ствараючы системы семантнай версіявання. Асалёйныя труднасці крыюцца ў коордынацыі: разработчыкам патрэбна спяльная адразумеласць пра тое, калі # є обавязковым, а калі private застаецца прыемным.

    Практычная практіка, яку адхоўвае многа каманд, выглядае так:

    • Заказваць # для токэнаў аутантыкацыі, ключоў шифравання, даных прыўязкі да базы дадзеных і персональных ідэнтыфікацыйных даных.
    • Вжываць private для слоёў кэшавання, стану налашчэнняў, внутрэшніх машын стану і вырачаных або падсумаваных значэнняў.
    • Запісваць праблематыку ў чэк-ліст для адзначэння коду і у дакументах падготовкі, каб новыя спецыялісты быстра зразумелі гэтыя правіла.
  • Дадзіце правілы адзорвання, які выяўляюць рызыковыя патэрны, напрыклад, поле пароля, заданае з private у зменшыці до #.
  • Арганізацыі, якія падтрымваюць як TypeScript, так і стары JavaScript у аднам рэпазітарые, патрабуець адносна іншы набор правілаў. У манорэпазітарые, які спаўнае службы TypeScript з старэйшымі модулямі JavaScript, застосоўка полей # у всіх месцах не є практычной чераз выкліканыя імі затраты на транспіляцыю для коду, якому гэта не патрэбна. У такой схеме межа часто ляжыць на рэпазітарые або на рэвэрсе пакета: новапісаныя модулі TypeScript выкарыстоўваюць # для чулых дадзенняў, тым часам стары JavaScript застаецца такім, які ён є, пакуль яго нарэшце не перапішуць.

    Гібрыдная стратэгія такага рода выклікаецца ў распраўнечаных системах, якія спалучаюць строгую прыватнасьць пад час выканання ў дзеяных службах з контрактамі на рэвэле ціннасцяў у іншых, дзе певныя компаненты забезпечваюць строгае інкапсуляванне, а іншыя павольваюцца выключна на перакрытчыкавыя перагледы.

    Большая частка проблем з гэтай міграцыёй выходзіць з таго, што пераклад вважаецца чыста механічная процедура пошуку і замены. Замена private на # без паветранага аналізу кожнага корыстувальця гэтаго поля прыводзіць да тых, што не бачна. Вядома прыклад — інструмент для логавання, створанный на аднойчыні з викорыстанням рэфлексіі, які аналізуе атрыбуты об’екта, каб стварыць інфармацыю для діагностикі; калі гэтыя атрыбуты стаюць полямі #, яны зникаюць з гэтаго перыябрання, і інструмент логавання тых, што не бачна, втрачае можлівасць бачыць стан об’екта. Правильны спосаб — адкрываць неабяжлівыя даны через явныя методы-гетеры або структураваны інтэрфейс логавання, а не пасляваліцца на рэфлексію, каб адкрыць прыватныя внутрошнясці.

    Частае запытанні

    Магчыма сумешваць модифікаторы private і поля # у аднай класе?

    Так. TypeScript 5.7 і новэйшыя версіі дазваляюць адночаснае існаванне обох механізмаў у ўзеўмной дэфініцыі класа. Частая практыка — выкорыстоўваць private для деталяў рэалізацыі, да якіх можа ўсё ж патрабаваць тэставанне або інструменты дыбаггіну, тады калі # застаўляецца для некалькіх поль, якія должны захіщацца ад будзь-якага перагляду пад час выканання. Кампайлер абрабатвае іх як два разныя, але сумесныя системы візыбільнасці.

    Чы робяцься поль з # у старэйшых средах JavaScript, такіх як IE11?

    Не безпосередна. Калі цэль кампайлінгу — ES5 або ES2015, кампайлер TypeScript выкарыстоўвае поліфілы, адаснованыя на WeakMap, каб імітаваць працэс # поль, што прыносіць дадатковы розмер коду і паслабляе працэздатнась у час выканання. Якщо вам яшчэ трэба падтрымліваць старыя браузеры, краща застаўся на модифікаторах private і працаваць з правіламі, якія дыяюць толькі пад час кампайлінгу, замест таго каб платіць за додатковыя поліфілы.

    Што выдзейчаеся з # поль пад час серыялізаціі у формате JSON?

    JSON.stringify і падобныя з’ёмнікі проста не можу бачыць поля #, таму любой об’ект, які іх мае, будзе з’ёмнікованы без гэтых атрыбутаў усеўшы. Якщо вам патрэбна частка гэтага прыватнага стану ў выходным дадзенні, вам трэба яшчо ўскрыць яе — аднойчы через метод-гетер, аднойчы шляхом задання спецыяльнай рэалізацыі toJSON, якая точна кантролюе, што будзе включана.

    Чы можу падкласы выйсці да полей # класа-родзіча?

    Няма. Асобныя лякалкі ECMAScript належаць выключна класу, дзе яны адказаны, і гэтыя межы ўсёвышэй — падклас не можа чытаць або мяніць лякалкі # бацькага класа, нават опосередка захіщаных або публічных методаў. Гэта значныя разлікі з модифікаторамі private, дзе кампайлер TypeScript іноды дазваляе падкласу выйсці да гэтых лякалак у момент явных паставок типу.

    Чы трэба мне пераводзіць існуючыя кодавескі з лякалак private на лякалкі #?

    Толькі тады, калі існуе конкрэтная патрабовання — рэальная ахвяра з безпекай або справжня неабходнасць у захаванні прыватнасці пад час выконання. Паколькі міграцыя спрычынае значныя змены, якія паўтараюцца ў інструментах, прыемах серіялізацыі і проектаванні тэстаў, яе не можна адмахваць лёгкава. Для большасці внутрашняях прыемоў модифікаторы private вялікай меры забезпечваюць неабходную інкапсуляцыю, таму вартасць міграцыі няўартая. Перш за ўсё прабуйце перейсці на новы формат для спяльных бібліятэкаў, API, якія є доступнымі для сторонніх корыстувачаў, або модуляў, якія обробляюць чутлівыя даны.

    Выбор правильнага модэлю прыватнасці для вашай базы коду у 2026 годзе

    Выбір между атрыбутам private у TypeScript і атрыбутам # у ECMAScript не ўзначае простага ліктарскага выбору. Цей выбір вплывае на тое, чы рэалізуецца гарантія інкапсуляцыі пасля компілявання, як зовнішній код можа взаўсёды взаімадзейваць з вашымі класамі, а таксама на тое, як будуцьяя людзі, якія будуць трымаць код, зрозумеюць архітектурныя намеры.

    Вжывайце атрыбут private, калі вы контролюеце весь граф залежнасцяў і ставіце інструменты для разработчыкаў вышэй за строгія гарантіі пад час адработкі коду. Выбірайце атрыбут #, калі ваш код запускаецца ў ненадзорных средах або обробляе даны, якія ніколі не можаць быць выкананы через рэфлексію. Большасць рэальных кодавых баз у канечнай працы вымагае сумешчання обох атрыбутаў, якія прыменяюцца цэлесапраўеда, залежна ад таго, што представляе кожны з атрыбутаў.

    Неправильна прыемка гэтага рашэння частаюць у практычным выкарыстоўванні: інварыянты можу быць спакаленыя внаследке ад трапляючыхся змян, даныя прыўязкі можу выцекаць через інфраструктуру логавання, а наборы тэстаў можу востатку зусім не магчымаць пераканацца ў стане внутраніх элементаў. Усё гэта можна запобiec за дапамогою чыстых стандартаў і задокументаваных архітектурных правілаў.

    Команды, якія ствараюць внутрашняе абладнанне, зазвычай можу павергацца на модифікаторы private і выкарыстоўваць готовыя інструменты, створаныя на ўсаснове іх. Команды, якія выпускаюць публічныя бібліятэкі або працуюць у сферах з высокай ступенем небяпекі, патрабуюць гарантый працы, якія даюць поля #. Оба падходы аднакова добра падтрымваныя ў сучасной экосфере TypeScript — правы выбор залежыць ад вашага модэлю загроз і ступеня довер'я да корыстувачаў вашаг API.

    Супаканальная літэратура

  • Адаптация TypeScript: чаму команды пераходзяць за межы звычнага JavaScript — Дакладна аналіз тэхнічных прычын — ад безпекі типаў да інструментаў та робочых практык з AI — якія спакушаюць команды выбіраць TypeScript заместо звычнага JavaScript у сучасным разработкі.