ProgrammationDéveloppeur Frontend

Comment fonctionnent les modificateurs d'accès (public, private, protected) en TypeScript ? Quelles sont les subtilités de leur utilisation avec l'héritage et lors de la transpilation en JavaScript ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En TypeScript, les modificateurs d'accès sont utilisés pour restreindre la portée des propriétés et des méthodes dans les classes :

  • public — la propriété ou la méthode est accessible partout (par défaut).
  • private — accessible uniquement dans la classe où elle est déclarée.
  • protected — accessible dans la classe et ses sous-classes.
class Animal { public name: string; private age: number; protected kind: string; constructor(name: string, age: number, kind: string) { this.name = name; this.age = age; this.kind = kind; } } class Dog extends Animal { bark() { console.log(this.kind); // OK: protected // console.log(this.age); // Erreur: private non visible dans la sous-classe } } const dog = new Dog('Sharik', 5, 'mammifère'); console.log(dog.name); // OK // console.log(dog.kind); // Erreur: protected // console.log(dog.age); // Erreur: private

Subtilités :

  • Dans le JavaScript compilé, les modificateurs private et protected ne sont appliqués qu'au moment de la compilation, il n'y a pas d'isolation physique à l'exécution ! Le code JS contiendra toutes les propriétés (elles peuvent être accessibles via une itération sur l'objet).
  • Dans les nouvelles versions (avec TypeScript 3.8), une syntaxe pour de véritables champs privés a été introduite : #field, mais c'est déjà une spécification JS, et elle est compilée différemment.

Question piégeante

Question : Peut-on accéder à une propriété private d'une classe TypeScript après sa compilation en JavaScript ?

Réponse : Oui, car private (et protected) est une vérification TypeScript au moment de la compilation, après la compilation en ES5 ou ES6, la confidentialité n'est pas maintenue, les propriétés restent dans l'objet et sont accessibles par l'accès au nom de la propriété (par exemple, via object['privateProp']).

// Code JS après la compilation function Animal(name, age, kind) { this.name = name; this.age = age; this.kind = kind; } var dog = new Animal('Sharik', 5, 'mammifère'); console.log(dog['age']); // 5 — pas d'accès uniquement au niveau TS !

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet.


Histoire

Dans un grand projet, un développeur comptait sur le fait que les champs private seraient inaccessibles à l'exécution. En conséquence, lors de la transmission d'un objet par sérialisation (JSON.stringify) dans les journaux, des données sensibles ont été accidentellement enregistrées, car le typage TS n'a pas protégé contre l'accès réel aux champs.


Histoire

Dans le projet, un mécanisme d'"extension" des instances de classes a été mis en œuvre par l'ajout dynamique de propriétés. Les propriétés ajoutées dynamiquement écrasaient les noms private, et elles étaient accidentellement modifiées par un code externe. L'erreur n'a été découverte qu'en production, la confidentialité n'ayant pas été assurée.


Histoire

Lors de la migration de JavaScript vers TypeScript, l'équipe a commencé à utiliser protected, pensant que cela protégeait contre l'utilisation des champs en dehors des sous-classes. Mais un programmeur a accidentellement écrasé un champ protected à l'exécution via Object.assign, entraînant un bug difficile à déceler. Cela est devenu possible parce qu'il n'y avait pas d'encapsulation à l'exécution.