Menu

Modificatori di accesso in TypeScript: public, private, protected

TypeScript ha tre modificatori di accesso, public, private e protected, più readonly. Scopri cosa consente ciascuno, perché private di TypeScript è un controllo in compilazione mentre i campi #private di JavaScript vengono imposti a runtime, e quale scegliere.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

TypeScript ha tre modificatori di accesso per i membri di una classe: public (il predefinito), protected e private. Controllano dove si può usare un membro, e il compilatore segnala qualsiasi accesso dal posto sbagliato.

L'ultima riga è un errore di compilazione, ma guarda cosa ha stampato eseguendola comunque: 123-45-6789. È il fatto più importante di questa pagina, e la sezione su private lo spiega.

Cosa consente ogni modificatore

ModificatoreDentro la classeIn una sottoclasseDa fuoriImposto a runtime
public (predefinito)sìsìsìniente da imporre
protectedsìsìnono
privatesìnonono
#name (JavaScript)sìnonosì
readonlylettura, e scrittura nel costruttoreletturaletturano

I modificatori funzionano su campi, metodi, getter e setter, costruttori e parameter property (constructor(private id: string)). Scrivere public è facoltativo, e molti codebase lo omettono.

private è un controllo in compilazione

TypeScript cancella private insieme al resto dei tipi. La classe compilata ha una proprietà normale, quindi tutto ciò che non passa dal type checker la vede: chiamanti in JavaScript semplice, JSON.stringify, Object.keys e perfino la notazione con parentesi quadre di TypeScript stesso, permessa di proposito come via di fuga.

Va bene per lo scopo di private: dire agli altri sviluppatori (e al tuo editor) che un membro è un dettaglio di implementazione. Non è un confine di sicurezza, e un campo private finirà nei log e nelle risposte JSON se non lo togli tu.

Campi #private: imposti a runtime

JavaScript ha i propri campi privati, scritti con #. È il motore a imporli: fuori dalla classe, obj.#field non è nemmeno sintassi valida, e il campo non compare in Object.keys, JSON.stringify o console.log.

Scrivere s.#token fuori dalla classe è l'errore TS18013 (Property '#token' is not accessible outside class 'Session' because it has a private identifier), e a differenza del caso private non c'è modo di aggirarlo nemmeno a runtime. Vedi i campi privati in JavaScript per le regole a runtime.

private vs #private: quale usare

private x#x
Controllato dail compilatoreil motore JavaScript
Visibile a Object.keys / JSON.stringifysìno
Accesso con parentesi obj["x"]consentitonon possibile
Una sottoclasse può dichiarare il proprio xno, va in conflittosì, ogni classe ha il proprio #x
Copiato da uno spread { ...obj }sìno
Sintassi sui metodiprivate helper()#helper()

Usa #private quando i dati devono restare privati a runtime (token, stato interno che gli utenti di una libreria non devono toccare) o quando vuoi che JSON.stringify li lasci fuori. Usa private quando l'obiettivo è solo un'API pubblica pulita, quando un framework deve leggere il campo per riflessione, o per seguire lo stile esistente di un codebase. Non combinarli: private #x è l'errore TS18010 (An accessibility modifier cannot be used with a private identifier).

protected e sottoclassi

Un membro protected è disponibile dentro la classe e in qualsiasi classe che la estende, ma non sulle istanze da fuori.

Una regola sorprende molti. Dentro Polygon puoi leggere sides su this o su un altro Polygon, ma non su un semplice Shape passato come parametro: other.sides con other: Shape è l'errore TS2446 (Property 'sides' is protected and only accessible through an instance of class 'Polygon'. This is an instance of class 'Shape'.). Una sottoclasse può raggiungere i membri protected solo degli oggetti che appartengono al suo ramo della gerarchia.

Una sottoclasse può rendere pubblico un membro protected ridichiarandolo, ma non può rendere protected o private un membro pubblico. Ridichiaralo con un inizializzatore (public override sides = 6) o solo come tipo (declare public sides: number). Un semplice public sides: number; viene rifiutato con TS2564 e TS2612, perché con i class field moderni reimposterebbe il valore ereditato a undefined dopo il ritorno di super().

readonly

readonly impedisce la riassegnazione dopo la costruzione. Il campo si può impostare nella dichiarazione o nel costruttore; qualsiasi assegnazione successiva è l'errore TS2540.

Qui emergono due limiti. readonly è superficiale: l'array non si può sostituire, ma il suo contenuto può cambiare (tipizzalo readonly string[] per bloccare push). E come private, sparisce a runtime, quindi l'assegnazione rifiutata dal compilatore è stata eseguita lo stesso. Per un valore che non deve cambiare a runtime, usa Object.freeze o un getter senza setter. La pagina su readonly tratta Readonly<T> e gli array readonly.

readonly si combina con i modificatori di accesso: private readonly cache = new Map<string, number>() è un pattern comune per un campo interno che non viene mai riassegnato.

Errori comuni

  • Trattare private come sicurezza. A runtime non c'è più. Usa #field per i dati che devono restare nascosti, e non passare mai un oggetto con dei segreti direttamente a JSON.stringify.
  • Scrivere public ovunque. È il predefinito; aggiungerlo non cambia nulla.
  • Usare protected per tutto ciò che è "interno". Se nessuna sottoclasse ne ha bisogno, private tiene la superficie più piccola.
  • Aspettarsi che readonly congeli i dati annidati. Impedisce solo la riassegnazione della proprietà stessa.

Domande frequenti

Quali sono i modificatori di accesso in TypeScript?

public (il predefinito: accessibile ovunque), protected (dentro la classe e le sue sottoclassi) e private (solo dentro la classe). readonly è un modificatore separato che impedisce la riassegnazione dopo il costruttore e si combina con uno qualsiasi dei tre.

Che differenza c'è tra private e #private in TypeScript?

private viene controllato solo dal compilatore; il JavaScript emesso ha una proprietà normale, quindi obj["secret"], JSON.stringify e Object.keys la vedono comunque. #secret è un campo privato di JavaScript: lo impone il runtime e il codice fuori dalla classe non può leggerlo in alcun modo.

private in TypeScript è davvero privato?

Solo in compilazione. Dopo la compilazione il campo è una proprietà normale che qualsiasi codice JavaScript può leggere. TypeScript permette perfino l'accesso con le parentesi quadre (obj["field"]) ai membri privati come via di fuga. Usa #field quando la privacy deve reggere a runtime.

Che differenza c'è tra protected e private in TypeScript?

Un membro private è visibile solo dentro la classe che lo dichiara. Un membro protected è visibile anche dentro le sottoclassi. Nessuno dei due è accessibile su un'istanza da fuori della gerarchia di classi.

Le proprietà readonly si possono modificare in TypeScript?

Una proprietà readonly si può assegnare nella sua dichiarazione o nel costruttore, e da nessun'altra parte (altrimenti TS2540). È superficiale: un readonly tags: string[] non si può sostituire, ma tags.push() funziona ancora. Usa readonly string[] per bloccare anche quello.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA