TypeScript a trois modificateurs d'accès pour les membres de classe : public (par défaut), protected et private. Ils contrôlent où un membre peut être utilisé, et le compilateur signale tout accès depuis un endroit interdit.
La dernière ligne est une erreur de compilation, mais regardez ce qu'elle a affiché une fois exécutée malgré tout : 123-45-6789. C'est le fait le plus important de cette page, et la section sur private l'explique.
Ce qu'autorise chaque modificateur
| Modificateur | Dans la classe | Dans une sous-classe | Depuis l'extérieur | Imposé à l'exécution |
|---|---|---|---|---|
public (par défaut) | oui | oui | oui | rien à imposer |
protected | oui | oui | non | non |
private | oui | non | non | non |
#name (JavaScript) | oui | non | non | oui |
readonly | lecture, et écriture dans le constructeur | lecture | lecture | non |
Les modificateurs s'appliquent aux champs, aux méthodes, aux getters et setters, aux constructeurs et aux propriétés de paramètres (constructor(private id: string)). Écrire public est facultatif, et beaucoup de bases de code l'omettent.
private est une vérification à la compilation
TypeScript efface private avec le reste des types. La classe compilée contient une propriété ordinaire, donc tout ce qui ne passe pas par le vérificateur de types la voit : les appelants en JavaScript pur, JSON.stringify, Object.keys, et même la notation à crochets de TypeScript, autorisée comme échappatoire volontaire.
Cela convient à ce pour quoi private existe : indiquer aux autres développeurs (et à votre éditeur) qu'un membre est un détail d'implémentation. Ce n'est pas une frontière de sécurité, et un champ private finira dans les logs et les réponses JSON si vous ne le retirez pas vous-même.
Les champs #private : imposés à l'exécution
JavaScript a ses propres champs privés, écrits avec #. Le moteur les impose : hors de la classe, obj.#field n'est même pas une syntaxe valide, et le champ n'apparaît ni dans Object.keys, ni dans JSON.stringify, ni dans console.log.
Écrire s.#token hors de la classe provoque l'erreur TS18013 (Property '#token' is not accessible outside class 'Session' because it has a private identifier), et contrairement au cas de private, il n'existe aucun contournement à l'exécution non plus. Voir champs privés en JavaScript pour les règles d'exécution.
private ou #private : lequel utiliser
private x | #x | |
|---|---|---|
| Vérifié par | le compilateur | le moteur JavaScript |
Visible par Object.keys / JSON.stringify | oui | non |
Accès par crochets obj["x"] | autorisé | impossible |
Une sous-classe peut déclarer son propre x | non, conflit | oui, chaque classe a son propre #x |
Copié par un étalement { ...obj } | oui | non |
| Syntaxe sur les méthodes | private helper() | #helper() |
Utilisez #private quand les données doivent rester privées à l'exécution (tokens, état interne auquel les utilisateurs d'une bibliothèque ne doivent pas toucher) ou quand vous voulez que JSON.stringify les ignore. Utilisez private quand le but est seulement une API publique propre, quand un framework doit lire le champ par réflexion, ou pour suivre le style existant d'une base de code. Ne combinez pas les deux : private #x provoque l'erreur TS18010 (An accessibility modifier cannot be used with a private identifier).
protected et les sous-classes
Un membre protected est disponible dans la classe et dans toute classe qui l'étend, mais pas sur les instances depuis l'extérieur.
Une règle surprend. Dans Polygon, vous pouvez lire sides sur this ou sur un autre Polygon, mais pas sur un simple Shape reçu en paramètre : other.sides avec other: Shape provoque l'erreur TS2446 (Property 'sides' is protected and only accessible through an instance of class 'Polygon'. This is an instance of class 'Shape'.). Une sous-classe ne peut atteindre les membres protected que des objets qui appartiennent à sa propre branche de la hiérarchie.
Une sous-classe peut rendre public un membre protected en le redéclarant, mais elle ne peut pas rendre protected ou private un membre public. Redéclarez-le avec un initialiseur (public override sides = 6) ou uniquement en tant que type (declare public sides: number). Un simple public sides: number; est refusé avec TS2564 et TS2612, car avec les champs de classe modernes il remettrait la valeur héritée à undefined après le retour de super().
readonly
readonly bloque la réassignation après la construction. Le champ peut être défini dans sa déclaration ou dans le constructeur ; toute assignation ultérieure provoque l'erreur TS2540.
Deux limites apparaissent ici. readonly est superficiel : le tableau ne peut pas être remplacé, mais son contenu peut changer (typez-le readonly string[] pour bloquer push). Et comme private, il disparaît à l'exécution : l'assignation refusée par le compilateur s'est donc quand même exécutée. Pour une valeur qui ne doit pas changer à l'exécution, utilisez Object.freeze ou un getter sans setter. La page readonly présente Readonly<T> et les tableaux readonly.
readonly se combine avec les modificateurs d'accès : private readonly cache = new Map<string, number>() est un motif courant pour un champ interne jamais réassigné.
Erreurs fréquentes
- Considérer
privatecomme une sécurité. Il disparaît à l'exécution. Utilisez#fieldpour les données qui doivent rester cachées, et n'envoyez jamais un objet contenant des secrets directement àJSON.stringify. - Écrire
publicpartout. C'est la valeur par défaut ; l'ajouter ne change rien. - Utiliser
protectedpour tout ce qui est « interne ». Si aucune sous-classe n'en a besoin,privategarde la surface plus réduite. - Attendre de
readonlyqu'il gèle les données imbriquées. Il empêche seulement la réassignation de la propriété elle-même.
Questions fréquentes
Quels sont les modificateurs d'accès en TypeScript ?
public (par défaut : accessible partout), protected (dans la classe et ses sous-classes) et private (dans la classe uniquement). readonly est un modificateur distinct qui bloque la réassignation après le constructeur et se combine avec chacun des trois.
Quelle est la différence entre private et #private en TypeScript ?
private n'est vérifié que par le compilateur ; le JavaScript émis contient une propriété ordinaire, donc obj["secret"], JSON.stringify et Object.keys la voient toujours. #secret est un champ privé JavaScript : l'environnement d'exécution l'impose, et le code extérieur à la classe ne peut pas du tout le lire.
private est-il vraiment privé en TypeScript ?
Seulement à la compilation. Après la compilation, le champ est une propriété normale que n'importe quel code JavaScript peut lire. TypeScript autorise même l'accès par crochets (obj["field"]) aux membres privés comme échappatoire. Utilisez #field quand la confidentialité doit tenir à l'exécution.
Quelle est la différence entre protected et private en TypeScript ?
Un membre private n'est visible que dans la classe qui le déclare. Un membre protected est aussi visible dans les sous-classes. Aucun des deux n'est accessible sur une instance depuis l'extérieur de la hiérarchie de classes.
Peut-on modifier une propriété readonly en TypeScript ?
Une propriété readonly peut être assignée dans sa déclaration ou dans le constructeur, et nulle part ailleurs (sinon TS2540). Elle est superficielle : un readonly tags: string[] ne peut pas être remplacé, mais tags.push() fonctionne toujours. Utilisez readonly string[] pour bloquer cela aussi.