Classes

Références

L'actualité

Librairie

L'information

Introduction

Le code JavaScript traditionnel utilise des fonctions et un héritage basé sur un prototype pour créer des composants réutilisables, mais cela peut sembler un peu gênant pour les programmeurs plus à l'aise avec une approche orientée objet, dans laquelle les classes héritent des fonctionnalités et les objets sont construits à partir de ces classes.

À partir de ECMAScript 2015, également appelé ECMAScript 6, les programmeurs JavaScript pourront créer leurs applications à l'aide de cette approche basée sur les classes orientée objet. Dans TypeScript, nous autorisons les développeurs à utiliser maintenant ces techniques et à les compiler en JavaScript qui fonctionne sur tous les principaux navigateurs et plates-formes, sans avoir à attendre la prochaine version de JavaScript.

Des classes

Jetons un coup d'oeil à un exemple simple basé sur une classe :

La syntaxe devrait sembler familière si vous avez déjà utilisé C# ou Java. Nous déclarons une nouvelle classe Greeter. Cette classe a trois membres : une propriété appelée greeting, un constructeur et une méthode greet.

Vous remarquerez que dans la classe, lorsque nous nous référons à l'un des membres de la classe, nous ajoutons this au début. Cela indique qu'il s'agit d'un accès membre.

Dans la dernière ligne, nous construisons une instance de la classe Greeter en utilisant new. Cela appelle le constructeur que nous avons défini précédemment, créant un nouvel objet avec la forme de Greeter et exécutant le constructeur pour l'initialiser.

Héritage

Dans TypeScript, nous pouvons utiliser des modèles communs orientés objet. L'un des modèles les plus fondamentaux de la programmation par classes est la possibilité d'étendre les classes existantes pour en créer de nouvelles en utilisant l'héritage.

Prenons un exemple :

Cet exemple montre la fonctionnalité d'héritage la plus élémentaire : les classes héritent des propriétés et des méthodes des classes de base.
Ici, Dog est une classe dérivée qui dérive de la classe de base Animal à l'aide du mot-clé extend.
Les classes dérivées sont souvent appelées sous-classes et les classes de base, souvent appelées superclasses.

Parce que Dog étend les fonctionnalités de Animal, nous avons pu créer une instance de Dog pouvant bark() et move().

Voyons maintenant un exemple plus complexe.

Cet exemple couvre quelques autres fonctionnalités que nous n'avions pas mentionnées auparavant. Encore une fois, nous voyons les mots-clés extend utilisés pour créer deux nouvelles sous-classes de Animal, Horse et Snake.

Une différence par rapport à l'exemple précédent est que chaque classe dérivée contenant une fonction constructeur doit appeler super() qui exécutera le constructeur de la classe de base. De plus, avant d'accéder à une propriété dans un corps de constructeur, nous devons appeler super(). Il s'agit d'une règle importante que TypeScript appliquera.

L'exemple montre également comment redéfinir les méthodes de la classe de base par des méthodes spécialisées pour la sous-classe. Ici, Snake et Horse créent tous deux une méthode de déplacement qui remplace le déplacement de Animal, ce qui lui confère une fonctionnalité propre à chaque classe. Notez que même si tom est déclaré comme un Animal, puisque sa valeur est un Horse, l'appel de tom.move(34) appellera la méthode prépondérante dans Horse :


public, private, protected


public

Dans nos exemples, nous avons pu accéder librement aux membres que nous avons déclarés tout au long de nos programmes. Si vous connaissez bien les classes dans d'autres languages, vous avez peut-être remarqué, dans les exemples ci-dessus, que nous n'avons pas eu à utiliser le mot public pour accomplir cela; Par exemple, C# exige que chaque membre soit explicitement étiqueté comme public pour être visible. Dans TypeScript, chaque membre est public par défaut.

Vous pouvez toujours marquer un membre public explicitement. Nous aurions pu écrire la classe Animal de la section précédente de la manière suivante :

private

Lorsqu'un membre est marqué comme private, il est impossible d'y accéder de l'extérieur de la classe qui le contient. Par exemple:

TypeScript est un système de types structurel. Lorsque nous comparons deux types différents, quelle que soit leur origine, si les types de tous les membres sont compatibles, nous disons que les types eux-mêmes sont compatibles.

Cependant, lorsque vous comparez des types qui ont des membres private et protected nous traitons ces types différemment. Pour que deux types soient considérés comme compatibles, si l'un d'eux a un membre private, l'autre doit avoir un membre private issu de la même déclaration. La même chose s'applique aux membres protected.

Voyons un exemple pour mieux voir comment cela se passe dans la pratique:

Dans cet exemple, nous avons un Animal et un Rhino, le Rhino étant une sous-classe de Animal. Nous avons également une nouvelle classe Employee qui ressemble à Animal en termes de forme. Nous créons certaines instances de ces classes et essayons ensuite de les assigner les unes aux autres pour voir ce qui va se passer. Comme Animal et Rhino partagent le même aspect privé de la même déclaration de nom privé: string dans Animal, ils sont compatibles. Cependant, ce n'est pas le cas pour l'employé. Lorsque nous essayons d'affecter un Employee à un Animal, nous obtenons une erreur indiquant que ces types ne sont pas compatibles. Même si Employee a également un name private, ce n'est pas celui que nous avons déclaré dans Animal.

protected

Le modificateur protected agit un peu comme le modificateur private, à l'exception du fait que les membres déclarés protected sont également accessibles dans les classes dérivées. Par exemple,

Notez que, même si nous ne pouvons pas utiliser le nom en dehors de Person, nous pouvons néanmoins l'utiliser à partir d'une méthode d'instance de Employee, car Employee dérive de Person.

Un constructeur peut également être marqué comme protected. Cela signifie que la classe ne peut pas être instanciée en dehors de la classe qui la contient, mais qu'elle peut être étendue. Par exemple,


readonly

Vous pouvez créer des propriétés en lecture seule à l'aide du mot clé readonly. Les propriétés en lecture seule doivent être initialisées lors de leur déclaration ou dans le constructeur.


Propriétés du paramètre

Dans notre dernier exemple, nous avons dû déclarer un name de membre en lecture seule et un paramètre de constructeur theName dans la classe Octopus, puis nous avons immédiatement défini name sur theName. Cela s'avère être une pratique très courante. Les propriétés des paramètres vous permettent de créer et d'initialiser un membre à un endroit. Voici une nouvelle révision de la classe Octopus précédente utilisant une propriété de paramètre :

Remarquez comment nous avons complètement abandonné theName et utilisons simplement le paramètre abrégé name: string du constructeur pour créer et initialiser le name du membre. Nous avons regroupé les déclarations et les assignations en un seul emplacement.

Les propriétés de paramètre sont déclarées en préfixant un paramètre de constructeur avec un modificateur d'accessibilité ou en lecture seule, ou les deux. L'utilisation de private pour une propriété de paramètre déclare et initialise un membre private de même, la même chose est faite pour public, protected et readonly.

Accesseurs

TypeScript supporte les getters / setters comme moyen d'intercepter les accès à un membre d'un objet. Cela vous donne un moyen d'avoir un contrôle plus fin sur la façon dont un membre est accédé sur chaque objet.

Convertissons une classe simple en utilisant get et set. Tout d'abord, commençons par un exemple sans getters et setters.

Permettre aux utilisateurs de définir directement et de manière aléatoire le nom complet fullName est très pratique, mais cela pourrait nous causer des problèmes si les gens pouvaient changer de name sur un coup de tête.

Dans cette version, nous vérifions que l'utilisateur dispose d'un code secret avant de lui permettre de modifier Employee. Nous faisons cela en remplaçant l'accès direct à fullName par un ensemble qui vérifie le code secret. Nous ajoutons un get correspondant pour permettre à l'exemple précédent de continuer à fonctionner de manière transparente.

Pour nous prouver que notre accesseur vérifie maintenant le code secret, nous pouvons le modifier et voir que, s'il ne correspond pas, nous recevons le message nous avertissant que nous n'avons pas le droit de mettre à jour Employee.

Quelques points à noter sur les accesseurs :

Tout d'abord, les accesseurs exigent que le compilateur produise une sortie ECMAScript 5 ou supérieure. La réduction à ECMAScript 3 n'est pas prise en charge. Deuxièmement, les accesseurs avec get et set sont automatiquement déduits d'être en lecture seule. Ceci est utile lors de la génération d'un fichier .d.ts à partir de votre code, car les utilisateurs de votre propriété peuvent voir qu'ils ne peuvent pas le modifier.

static

Jusqu'à présent, nous n'avions parlé que des instances membres de la classe, celles qui apparaissent sur l'objet lorsqu'il est instancié. Nous pouvons également créer des membres statiques d'une classe, ceux qui sont visibles sur la classe elle-même plutôt que sur les instances. Dans cet exemple, nous utilisons static sur origine, car c'est une valeur générale pour toutes les Grid. Chaque instance accéde à cette valeur en ajoutant le nom de la classe en préfixe. De la même façon, avant this. devant les accés aux instances, nous précédons ici Grid devant des accés static.


abstract

Les classes abstract sont des classes de base à partir desquelles d'autres classes peuvent être dérivées. Ils ne peuvent pas être instanciés directement. Contrairement à une interface, une classe abstract peut contenir des détails d'implémentation pour ses membres. Le mot clé abstract est utilisé pour définir des classes abstract ainsi que des méthodes abstract au sein d'une classe abstract.

Les méthodes d'une classe abstract marquées comme abstract ne contiennent pas d'implémentation et doivent être implémentées dans des classes dérivées. Les méthodes abstract partagent une syntaxe similaire aux méthodes d'interface. Les deux définissent la signature d'une méthode sans inclure un corps de méthode. Toutefois, les méthodes abstract doivent inclure le mot clé abstract et peuvent éventuellement inclure des modificateurs d'accès.


Techniques Avancées

Fonctions du constructeur

Lorsque vous déclarez une classe dans TypeScript, vous créez plusieurs déclarations en même temps. Le premier est le type de l'instance de la classe.

Ici, lorsque nous disons laisser greeter: Greeter, nous utilisons Greeter comme type d'instances de la classe Greeter. C'est presque une seconde nature pour les programmeurs d'autres langages orientés objet.

Nous créons également une autre valeur que nous appelons la fonction constructeur. C'est la fonction qui est appelée lorsque nous créons de nouvelles instances de la classe. Pour voir à quoi cela ressemble dans la pratique, jetons un coup d'oeil au JavaScript créé par l'exemple ci-dessus:

Ici, laissez Greeter va se voir attribuer la fonction constructeur. Lorsque nous appelons new et exécutons cette fonction, nous obtenons une instance de la classe. La fonction constructeur contient également tous les membres static de la classe. Une autre façon de penser à chaque classe est qu'il existe un côté instance et un côté static.

Modifions un peu l'exemple pour montrer cette différence :

Dans cet exemple, greeter1 fonctionne de la même manière que précédemment. Nous instancions la classe Greeter et utilisons cet objet. Nous l'avons vu auparavant.

Ensuite, nous utilisons directement la classe. Ici, nous créons une nouvelle variable appelée greeterMaker. Cette variable tiendra la classe elle-même, ou dit autrement sa fonction constructeur. Ici, nous utilisons typeof Greeter, c'est-à-dire "donnez-moi le type de la classe Greeter elle-même" plutôt que le type d'instance. Ou, plus précisément, "donnez-moi le type du symbole appelé Greeter", qui est le type de la fonction constructeur. Ce type contiendra tous les membres static de Greeter ainsi que le constructeur qui crée des instances de la classe Greeter. Nous montrons cela en utilisant new sur greeterMaker, en créant de nouvelles instances de Greeter et en les invoquant comme auparavant.

Utiliser une classe comme interface

Comme nous l'avons dit dans la section précédente, une déclaration de classe crée deux choses : un type représentant des instances de la classe et une fonction constructeur. Comme les classes créent des types, vous pouvez les utiliser aux mêmes endroits que vous pourriez utiliser des interfaces.