ProgrammazioneSviluppatore Fullstack

Come funziona il meccanismo dei Campi Privati (proprietà private) nelle classi TypeScript? Qual è la differenza tra le proprietà private utilizzando il modificatore private e il simbolo #, quali sono le peculiarità della loro applicazione e le limitazioni?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Storia della questione

In TypeScript, le proprietà private sono state introdotte inizialmente tramite il modificatore private, mentre a partire da ECMAScript 2021 in JavaScript sono supportati i veri campi chiusi attraverso il simbolo #. L'obiettivo principale è l'incapsulamento dei dati, al fine di proteggere i dettagli interni della classe dall'accesso esterno.

Problema

Il modificatore private in TypeScript garantisce la privacy solo a livello di compilazione, ma nel JavaScript compilato questi campi rimangono accessibili tramite accesso diretto alle proprietà dell'oggetto. Questo può portare a modifiche indesiderate dello stato dell'oggetto. I Campi Privati ES (#) sono completamente inaccessibili al di fuori della classe anche a livello di runtime JS, fornendo così una vera protezione dei dati.

Soluzione

TypeScript supporta entrambi gli approcci. Quando si sceglie tra di essi, è importante orientarsi sul grado di protezione necessario e sulle limitazioni di compatibilità con le versioni JS.

Esempio di codice:

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 — errore in TS, ma funziona tramite x['hidden'] in JS // x.#trulyHidden — errore di sintassi anche in JS

Caratteristiche chiave:

  • private garantisce la privacy solo a livello di compilazione di TypeScript
  • #field fornisce una vera privacy a livello di esecuzione in JavaScript
  • L'uso del # richiede il supporto di un JavaScript moderno (ES2021+)

Domande trabocchetto.

È possibile accedere a un campo privato di una classe TypeScript tramite accesso diretto in JS?

Sì, è possibile, poiché la privacy è implementata solo a livello di compilazione. Nel JS sorgente, il campo sarà una normale proprietà dell'oggetto. Ad esempio:

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

È possibile utilizzare campi privati con # nelle interfacce o descriverli tramite type?

No, i campi #private sono parte solo dell'implementazione della classe e non possono essere descritti nelle interfacce, nei tipi o utilizzati al di fuori della classe stessa. Le interfacce descrivono solo membri pubblici.

È possibile ereditare campi privati, dichiarati con #?

No, tali campi non sono affatto accessibili alle classi figlie. Solo la classe stessa ha accesso ad essi:

class Parent { #foo = 123; } class Child extends Parent { // this.#foo = 444; // Errore }

Errori tipici e anti-pattern

  • Utilizzare private per proteggere dati critici — porta a vulnerabilità con accesso diretto
  • Confusione tra privacy a livello di tipi e runtime
  • Tentativo di utilizzare #fields in versioni più vecchie di JS

Esempio dalla vita reale

Caso negativo

Nel progetto per la memorizzazione delle password nella classe User è stato utilizzato il campo private password. Uno degli sviluppatori ottiene accidentalmente accesso tramite user['password'] per il debugging, e il campo viene modificato da un modulo esterno.

Vantaggi:

  • Integrazione semplice con TypeScript

Svantaggi:

  • Nessuna protezione reale a livello di runtime JS
  • Facile bypass della privacy accidentalmente

Caso positivo

Lo sviluppatore utilizza il campo privato ES #password. Ora i tentativi di accesso al campo tramite indicizzazione o nelle classi figlie non sono consentiti, la sicurezza dei dati è garantita anche con l'uso di librerie e strumenti di terze parti.

Vantaggi:

  • Vera privacy
  • Protezione affidabile delle proprietà sensibili

Svantaggi:

  • Richiede supporto per un JavaScript moderno (non compatibile con ES5)
  • I campi privati non sono visibili nelle interfacce e non possono essere sovrascritti nelle classi figlie