Typescript: Prise en charge des classes finales (non sous-classables)

Créé le 26 avr. 2016  ·  124Commentaires  ·  Source: microsoft/TypeScript

Je pensais qu'il pourrait être utile d'avoir un moyen de spécifier qu'une classe ne doit pas être sous-classée, afin que le compilateur avertisse l'utilisateur lors de la compilation s'il voit une autre classe étendre l'originale.

Sur Java, une classe marquée avec final ne peut pas être étendue, donc avec le même mot-clé sur TypeScript, cela ressemblerait à ceci :

final class foo {
    constructor() {
    }
}

class bar extends foo { // Error: foo is final and cannot be extended
    constructor() {
        super();
    }
}
Suggestion Won't Fix

Commentaire le plus utile

Il devrait également y avoir un modificateur final pour les méthodes :

class Foo {
  final fooIt():void{

  }
}

class Bar {
  fooIt():void {

  }
}
// => Method fooIt of Bar cannot override fooIt of Foo, because it is final.

Par exemple, j'utilise souvent le modèle suivant, où je veux éviter de toute urgence fooIt d'être remplacé :

import Whatever ...

abstract class Foo {
  private ImportantVariable:boolean;

  protected abstract fooIt_inner:Whatever();

  public final fooIt():Whatever() {
    //do somestate change to aprivate member here, which is very crucial for the functionality of every Foo:
    ImportantVariable = true;
    //call the abstract inner functionality:
    return this.fooIt_inner();    
  }
}

Tous les 124 commentaires

une classe avec un constructeur privé n'est pas extensible. pensez à l'utiliser à la place.

D'après ce dont je me suis souvenu, j'étais sûr que le compilateur n'aimait pas le mot-clé private sur le constructeur. Peut-être que je n'utilise pas la version coller

Il s'agit d'une nouvelle fonctionnalité, qui sera publiée dans TS 2.0, mais vous pouvez l'essayer en utilisant typescript@next . voir https://github.com/Microsoft/TypeScript/pull/6885 pour plus de détails.

D'accord, merci

Le constructeur privé ne rend-il pas également une classe non instanciable à partir de la classe ? Ce n'est pas une bonne réponse à la classe finale.

Java et/ou C# utilise la classe final pour optimiser votre classe à l'exécution, sachant qu'elle ne sera pas spécialisée. je dirais que c'est la principale valeur du support final. Dans TypeScript, nous ne pouvons rien offrir pour que votre code s'exécute mieux qu'il ne l'a fait sans final.
Envisagez d'utiliser des commentaires pour informer vos utilisateurs de l'utilisation correcte de la classe et/ou de ne pas exposer les classes que vous souhaitez être finales, et exposez leurs interfaces à la place.

Je ne suis pas d'accord avec ça, au contraire je suis d'accord avec duanyao. Private ne résout pas ce problème, car je souhaite également que les classes finales soient instanciables à l'aide d'un constructeur. De plus, ne pas les exposer à l'utilisateur me forcerait à écrire des usines supplémentaires pour eux. Pour moi, la principale valeur du support final est qu'il empêche les utilisateurs de faire des erreurs.
Argumenter comme ça : que propose TypeScript pour accélérer l'exécution de mon code, lorsque j'utilise des types dans des signatures de fonction ? N'est-ce pas aussi seulement pour empêcher les utilisateurs de se tromper ? Je pourrais écrire des commentaires décrivant les types de valeurs qu'un utilisateur doit transmettre en tant que paramètre. C'est dommage que de telles extensions comme un mot-clé final soient simplement repoussées, car à mon avis, cela se heurte à l'intention originale de typescript : rendre JavaScript plus sûr en ajoutant un niveau de compilation, qui effectue autant de vérifications que possible pour éviter autant d'erreurs d'avance que possible. Ou ai-je mal compris l'intention de TypeScript ?

Il devrait également y avoir un modificateur final pour les méthodes :

class Foo {
  final fooIt():void{

  }
}

class Bar {
  fooIt():void {

  }
}
// => Method fooIt of Bar cannot override fooIt of Foo, because it is final.

Par exemple, j'utilise souvent le modèle suivant, où je veux éviter de toute urgence fooIt d'être remplacé :

import Whatever ...

abstract class Foo {
  private ImportantVariable:boolean;

  protected abstract fooIt_inner:Whatever();

  public final fooIt():Whatever() {
    //do somestate change to aprivate member here, which is very crucial for the functionality of every Foo:
    ImportantVariable = true;
    //call the abstract inner functionality:
    return this.fooIt_inner();    
  }
}

L'argument du coût par rapport à l'utilité est assez subjectif. La principale préoccupation est que chaque nouvelle fonctionnalité, construction ou mot-clé ajoute de la complexité au langage et à l'implémentation du compilateur/des outils. Ce que nous essayons de faire lors des réunions de conception de langage, c'est de comprendre les compromis et d'ajouter de nouvelles fonctionnalités uniquement lorsque la valeur ajoutée l'emporte sur la complexité introduite.

Le problème n'est pas verrouillé pour permettre aux membres de la communauté de continuer à ajouter des commentaires. Avec suffisamment de commentaires et des cas d'utilisation convaincants, les problèmes peuvent être rouverts.

En fait, final est un concept très simple, n'ajoute aucune complexité au langage et il devrait être ajouté. Au moins pour les méthodes. Cela ajoute de la valeur, quand beaucoup de gens travaillent sur un gros projet, il est utile de ne pas autoriser quelqu'un à outrepasser des méthodes, qui ne devraient pas être modifiées.

Dans TypeScript, nous ne pouvons rien offrir pour que votre code s'exécute mieux qu'il ne l'a fait sans final.

Waouh, craquez-vous ! Les types statiques n'améliorent pas non plus l'exécution de votre code, mais la sécurité est une bonne chose à avoir.

Final (scellé) est là-haut avec la substitution en tant que fonctionnalités que j'aimerais voir pour rendre les personnalisations de classe un peu plus sûres. Je ne me soucie pas des performances.

Les types statiques n'améliorent pas non plus l'exécution de votre code, mais la sécurité est une bonne chose à avoir.

Exactement. Tout comme private empêche les autres d'appeler la méthode, final empêche les autres de surcharger la méthode.

Les deux font partie de l'interface OO de la classe avec le monde extérieur.

Entièrement d'accord avec @pauldraper et @mindarelus. S'il vous plaît implémentez ceci, cela aurait beaucoup de sens, cela me manque vraiment actuellement.

Je ne pense pas que final soit uniquement bénéfique pour les performances, il est également bénéfique pour la conception, mais je ne pense pas que cela ait du tout de sens dans TypeScript. Je pense que cela est mieux résolu en suivant les effets de mutabilité de Object.freeze et Object.seal .

@aluanhaddad Pouvez-vous expliquer cela plus en détail ? Pourquoi pensez-vous que cela n'a "pas du tout de sens dans TypeScript" ?
Geler ou sceller un objet signifie interdire l'ajout de nouvelles propriétés à un objet, mais n'empêche pas d'ajouter des propriétés à un objet dérivé, donc même si je scellais la classe de base, je pourrais toujours remplacer la méthode dans une classe enfant, ce qui étend cette classe de base . De plus, je ne pouvais ajouter aucune propriété à la classe de base lors de l'exécution.

L'idée d'utiliser final sur une classe ou une méthode de classe en Java a plus à voir avec la minimisation de la mutabilité de l'objet pour la sécurité des threads à mon avis. (Article 15. Joshua Bloch, Java efficace)

Je ne sais pas si ces principes sont transférés dans javascript, car tout dans JS est modifiable (corrigez-moi si je me trompe). Mais Typescript n'est pas Javascript, n'est-ce pas ?

J'aimerais vraiment que cela soit mis en œuvre. Je pense que cela aidera à créer un code plus robuste. Maintenant... Comment cela se traduit en JS, honnêtement, ce n'est probablement pas nécessaire. Il peut simplement rester du côté dactylographié de la clôture où se trouve le reste de notre vérification au moment de la compilation.

Bien sûr, je peux m'en passer, mais cela fait partie de ce qu'est un tapuscrit, n'est-ce pas ? Revérifier nos erreurs négligées ?

Pour moi, final jouerait le même rôle dans le dactylographe que private ou les dactylographies, c'est-à-dire le contrat de code. Ils peuvent être utilisés pour garantir que votre contrat de code ne soit pas rompu. Je l'aimerais tellement.

@hk0i est également mentionné dans l'article 17 (2e édition) d'une manière similaire à ce qui a été repris ici :

Mais qu'en est-il des classes concrètes ordinaires ? Traditionnellement, ils ne sont ni définitifs ni conçus et documentés pour le sous-classement, mais cet état de fait est dangereux. Chaque fois qu'une modification est apportée à une telle classe, il est possible que les classes clientes qui étendent la classe se cassent. Ce n'est pas seulement un problème théorique. Il n'est pas rare de recevoir des rapports de bogues liés aux sous-classes après avoir modifié les éléments internes d'une classe concrète non finale qui n'a pas été conçue et documentée pour l'héritage.

La meilleure solution à ce problème est d'interdire le sous-classement dans des classes qui ne sont pas conçues et documentées pour être sous-classées en toute sécurité. Il existe deux manières d'interdire le sous-classement. Le plus simple des deux est de déclarer la classe finale. L'alternative est de rendre tous les constructeurs privés ou package-private et d'ajouter des usines statiques publiques à la place des constructeurs.

Je dirais que cela n'augmente pas la complexité cognitive de la langue étant donné que le mot-clé abstrait existe déjà. Cependant, je ne peux pas parler de son impact sur la mise en œuvre / les performances et je respecte absolument la protection du langage sous cet angle. Je pense que séparer ces préoccupations serait fructueux pour décider de mettre en œuvre ou non cette fonctionnalité.

Je crois que final serait un excellent ajout pour sceller une classe. Un cas d'utilisation est que vous pouvez avoir beaucoup de méthodes publiques dans votre classe mais exposer, via une interface, juste un sous-ensemble d'entre elles. Vous pouvez tester rapidement l'implémentation unitaire car elle possède toutes ces méthodes publiques tandis que le vrai consommateur utilise l'interface pour en limiter l'accès. Pouvoir sceller la mise en œuvre garantirait que personne ne prolonge la mise en œuvre ou ne modifie les méthodes publiques.

Vous pouvez également vous assurer que personne n'hérite de votre classe. TypeScript devrait être là pour appliquer ces règles, et la suggestion concernant les commentaires semble être une approche paresseuse pour résoudre ce cas d'utilisation. L'autre réponse que j'ai lue concerne l'utilisation de private qui ne convient qu'à une situation particulière, mais pas à celle que j'ai expliquée ci-dessus.

Comme beaucoup de personnes dans ce fil, je voterais pour pouvoir sceller la classe.

@mhegazy Les constructeurs privés et les classes scellées/finales ont une sémantique très différente. Bien sûr, je peux empêcher l'extension en définissant un constructeur privé, mais je ne peux pas non plus appeler le constructeur depuis l'extérieur de la classe, ce qui signifie que je dois ensuite définir une fonction statique pour permettre la création d'instances.

Personnellement, je recommanderais d'avoir des vérifications du compilateur scellées/finales pour s'assurer que les classes et les méthodes marquées scellées/finales ne peuvent pas être étendues ou remplacées.

Dans le cadre de mon problème, je veux pouvoir avoir un constructeur public pour pouvoir instancier l'objet, mais empêcher l'extension de la classe, et seul l'ajout de scellé/final le permettra.

Il y a une tâche - écrivez du code qui

  • exploite des objets immuables
  • l'héritage est-il libre
  • méthodes de réflexion gratuites

Et le mot-clé final est nécessaire ici, à mon humble avis.

Je vois final comme un excellent moyen de vous rappeler, ainsi qu'aux utilisateurs, de votre code quelles méthodes protégées ils devraient remplacer et lesquelles ne devraient pas. En remarque, je suis également partisan de déclarer explicitement que vous remplacez, en partie pour que le lecteur le sache, mais aussi pour que le transpileur se plaint si vous tapez le nom de la méthode.

@zigszigsdk Étant donné que les méthodes ne sont jamais remplacées, comment cela fonctionnerait-il ?

Pour final :
De la même manière que cela fonctionne maintenant, sauf que le transpiler se plaindrait si l'on cache la méthode d'un super -qui a été déclarée finale- du contexte this .

Pour override :
De la même manière que cela fonctionne maintenant, sauf que le transpiler se plaindrait si l'on déclarait une méthode override , et son super n'a pas de méthode de ce nom, ou il en a une mais il est déclaré être final .
Il peut également vous avertir si vous masquez une méthode de super du contexte this et que vous n'indiquez pas la substitution.

Java et/ou C# utilise la classe finale pour optimiser votre classe à l'exécution, sachant qu'elle ne sera pas spécialisée. je dirais que c'est la principale valeur du support final.

@mhegazy Pas tout à fait ! Une autre fonction importante est d'être explicite sur les parties de la classe qui doivent être remplacées.

Par exemple, voir l'article 17 dans « Java efficace » : « Concevoir et documenter pour l'héritage ou bien l'interdire » :

Il n'est pas rare de recevoir des rapports de bogues liés aux sous-classes après avoir modifié les éléments internes d'une classe concrète non finale qui n'a pas été conçue et documentée pour l'héritage. La meilleure solution à ce problème est d'interdire le sous-classement dans des classes qui ne sont pas conçues et documentées pour être sous-classées en toute sécurité. Il existe deux manières d'interdire le sous-classement. Le plus simple des deux est de déclarer la classe finale.

Idem pour les méthodes finales, à mon avis. Il est très rare qu'une classe soit conçue pour prendre en charge le remplacement de l' une de ses méthodes publiques. Cela nécessiterait une conception très complexe et une énorme quantité de tests, car vous devriez penser à toutes les combinaisons possibles d'états qui pourraient résulter d'une combinaison de comportements non remplacés et prioritaires. Ainsi, à la place, un programmeur pourrait déclarer toutes les méthodes publiques finales sauf une ou deux, ce qui réduirait considérablement le nombre de telles combinaisons. Et le plus souvent, une ou deux méthodes substituables sont précisément ce qui est nécessaire.

Je pense que les classes et méthodes final sont très importantes. J'espère que vous l'appliquerez.

Un autre cas utilisateur convaincant @mhegazy . Aujourd'hui, j'ai appris le modèle de méthode de modèle et j'ai découvert que final est nécessaire pour interdire aux sous-classes de modifier la méthode de modèle, comme le dit le modèle de méthode de modèle dans wikipedia . Mais je ne peux pas le faire dans mon adorable TypeScript. Quel dommage!

Le modèle de modèle permet aux sous-classes de redéfinir certaines étapes d'un algorithme sans modifier la structure de l'algorithme

Pour moi, c'est aussi simple que d'essayer d'imposer la composition dans les cas d'héritage. Sans final, vous ne pouvez pas faire cela.

Autant j'aimerais aussi voir final arriver au dactylographe, je pense que le message sous-jacent que Microsoft essaie de nous envoyer est que si nous voulons final , nous devrions utiliser un langage qui le supporte.

J'aimerais que ce problème soit rouvert et mis en œuvre.

Lors de la création d'une bibliothèque que vous allez utiliser en interne ou partager publiquement (comme sur npm), vous ne voulez pas que ceux qui l'utilisent aient à parcourir le code et à rechercher dans les commentaires ou les documents pour voir si la classe peut/peut ne soit pas écrasé. S'ils l'écrasent, une erreur est générée.

Dans mon code, j'ai une classe qui s'étend, et lorsqu'une sorte d'événement a lieu, elle déclenche une méthode. Si la sous-classe définit la méthode ce sera celle-là sinon elle retombera sur celle par défaut. Cependant, il existe également d'autres méthodes dans la classe qui ne sont pas des méthodes "de secours", ce sont plutôt des méthodes d'assistance telles que l'ajout d'un élément au DOM, auquel cas je ne veux pas que ces méthodes soient écrasées.

Mes 5 cents sur le sujet sont que cela devrait être rouvert et mis en œuvre.

Je l'utiliserais pour obliger les clients des bibliothèques à créer leurs propres classes concrètes et non à s'étendre à partir d'autres déjà existantes. Comme l'a dit @lucasyvas , privilégiez la composition. Ou appliquez simplement une nouvelle implémentation de classe concrète à fournir à l'injection de dépendance en angulaire par exemple.

Je vote également pour la réouverture, avec le support des classes et des méthodes

Je ne dis pas que nous ne devrions pas. Je ne suis simplement pas sûr de la façon dont le modèle du mot-clé final fonctionnerait dans le contexte du tapuscrit ?

Le mot-clé final n'empêcherait-il pas les utilisateurs/programmeurs de remplacer/hériter de ladite classe/méthode ?

Ma façon de voir Final est de "geler" les données, ce qui signifie qu'elles ne peuvent plus être modifiées. Je veux dire que cela peut être agréable à utiliser, bien que cela puisse être vraiment un problème pour les personnes qui souhaitent corriger ou modifier certains comportements.

@niokasgami Freeze est lié à [data] (https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/freeze)
et déjà disponible dans le cadre d'ES6. Final s'applique aux classes et aux méthodes (la variable peut être en lecture seule et donc agir comme "finale" car nous ne pouvons pas surcharger une super variable).

Le mot-clé "Final" est utilisé en JAVA où scellé est en C# .

Comme le script dactylographié est fortement inspiré de C#, il devrait y avoir une probabilité plus élevée que "scellé" soit celui choisi. Maintenant, nous pourrions éventuellement avoir une confusion avec le sceau ES6 qui scelle essentiellement l'objet au moment de l'exécution ?

@Xample Je pense que vous pouvez également utiliser final sur les variables locales en Java. Je me souviens avoir dû le faire quand j'écrivais une application Android. Je pense que cela m'a obligé à le faire avec un événement mais je ne suis pas sûr à 100%.

function(){
    let final myVariable = 'Awesome!'
    document.addEventListener('scroll', e => {
        console.log(myVariable)
        myVariable = 'Not Awesome' // Throws an error
    })
}

@TheColorRed Eh bien, la variable locale finale en Java signifie constante, n'est-ce pas?

@EternalPhane Je suppose que oui, j'ai toujours utilisé const dans une portée globale semblable... Je n'ai jamais pensé à l'utiliser dans une fonction ou une méthode... Je suppose que le commentaire précédent est nul alors...

@Xample Ouais, je vois votre point de vue pour Sealed from the C #, cela ne fonctionne pas de la même manière Honnêtement, à mon avis, les DEUX mots clés sceau et final ne pouvaient pas vraiment entrer dans la catégorie car les deux peuvent être déroutants (scellé pour sceau, etc.) et Final, nous ne sommes pas sûrs comment l'appliquer car en Java, il est utilisé comme une constante :/

En Java, vous pouvez final n'importe quelle variable, méthode ou classe. Pour les variables, cela signifie que les références ne peuvent pas être réaffectées (pour les types sans référence, cela signifie que vous ne pouvez pas non plus modifier leurs valeurs). Pour les classes, cela signifie pas d'extensions (mot-clé extends ...). Pour les méthodes, cela signifie qu'il n'y a pas de substitution dans les sous-classes.

Principales raisons de final en Java :

  • Réduire l'immuabilité des variables : pour la sécurité des threads
  • Finaliser les classes afin qu'elles ne puissent pas être héritées : force la composition sur l'héritage car l'héritage est intrinsèquement compliqué lorsque vos sous-classes commencent à appeler par inadvertance des super méthodes
  • Empêche les simples erreurs de programmation : si vous n'avez pas l'intention de changer une valeur, bonne nouvelle, ce n'est plus possible
  • Utilisé pour ce à quoi la plupart des gens pensent lorsqu'ils imaginent des constantes : des valeurs réutilisables que vous verrez partout, comme dans public static final String KEY = "someStringKeyPathHere" , utile pour réduire les erreurs mais a un avantage de temps de compilation supplémentaire de ne pas recréer plusieurs objets de chaîne pour la même valeur

J'ai peut-être omis quelques cas d'utilisation, mais j'essayais de rester très général et de donner quelques exemples.

@TheColorRed Android l'exige lorsque vous déclarez une variable en dehors d'un rappel (classe anonyme), puis que vous la référencez à l'intérieur. Je pense que cela correspond à nouveau aux problèmes de sécurité des threads. Je pense qu'ils essaient de vous empêcher de réaffecter la variable d'un autre thread.

Malgré son utilité, je ne pense pas que nous verrons cette fonctionnalité arriver de si tôt. Ce serait peut-être le bon moment pour examiner d'autres solutions ou, comme dans mon cas, supprimer le texte dactylographié et gérer le manque de rigueur dans JS. Javascript a beaucoup d'"avantages" en n'ayant aucune indication de ce type ou quoi que ce soit et finalement votre code sera converti en JS de toute façon. Si vous voulez une compatibilité avec les navigateurs, vous pouvez écrire ES6 et utiliser Babel pour le convertir.

Je pense que, de manière réaliste, même si vous pouvez donner une tape sur le poignet pour certains de ces avantages, vous n'obtenez aucun des avantages réels du java-esque final une fois qu'il a été converti en js.

@hk0i Je pense que je pourrais comprendre votre point de vue, mais dans l'ensemble, je suis d'accord et un peu en désaccord en même temps sur votre opinion.

Je dirais que la plupart du code dactylographié est destiné à des fins de débogage/suivi pour aider le programmeur à bien faire du code "propre" (veuillez ne pas me citer, je sais qu'ils ont du code spaghetti dactylographié ici hahah)

la plupart des fonctionnalités dactylographiées ne sont pas transpilées la plupart du temps lorsqu'elles sont converties en JS, car elles n'existent tout simplement pas dans JS.

Bien que lorsque vous exécutez/écrivez votre code, il est utile de voir les erreurs dans votre code et de les suivre plus facilement qu'avec JS (et HECK JS peut être difficile à suivre les bogues lol)

Je pense donc que l'utilisation du mot-clé final pourrait être considérée comme un bloqueur pour l'utilisateur de l'API : Oh d'accord, cette classe ne devrait pas être étendue car son extension pourrait provoquer un comportement étrange, etc.

Tapuscrit en tant que bon langage, je me trouve triste de constater qu'il manque une fonctionnalité importante telle que le mot-clé de classe statique "vrai" qui empêche les gens d'appeler le constructeur de classe tout en étant en mesure d'y accéder.

@hk0i Je ne suis pas d'accord non plus, le but du tapuscrit est de structurer le code et donc de fournir les outils pour le faire. Nous n'avons pas besoin d'utiliser final en Java, nous l'utilisons généralement lorsque nous savons que l'extension d'une classe ou d'une méthode pourrait entraîner l'introduction d'effets secondaires (ou pour toute autre raison de sécurité).

S'appuyer sur les solutions ES6 actuelles en ajoutant un contrôle de mutabilité à l'exécution (par exemple en utilisant Object.seal ou Object.freeze) vous fait perdre l'avantage d'avoir des erreurs directement lorsque vous tapez votre code (bien que cela puisse facilement être fait à l'aide de ce décorateur ).

@TheColorRed oui… et en tapuscrit nous avons une distinction pour les variables locales et de classe.

  • readonly pour une variable de classe
  • const : pour toute autre variable

Aucun d'entre eux ne serait précis pour la méthode ou la classe. Remplacer une méthode ne signifie pas que nous la redéfinissons (pensez aux méthodes virtuelles en C++), nous définissons simplement une méthode qui sera appelée principale avant la super (que nous pouvons appeler en utilisant super ).

@niokasgami peut-être que le nommage n'est pas vraiment un problème car nous comprendrons toujours que Object.seal s'appliquait à… un objet tout en scellant une classe et une méthode à la structure elle-même.

TL ; DR :

  • preventExtensions serait un bon nom explicite pour empêcher l'extension des classes
  • preventOverrides serait un bon nom candidat pour éviter de remplacer les classes
    MAIS : seal est largement utilisé en C#, plus court et a le même comportement attendu.

Au fait, j'ai oublié de mentionner qu'il y a aussi Object.preventExtensions()
Les différences sont visibles ici

en train de lire

  • toujours possible… qui veut une variable en écriture seule ?

mise à jour et suppression

  • empêcher la redéfinition d'une suppression de variable : en lecture seule pour un membre de la classe, const (pour tout autre)
  • empêcher de redéfinir une méthode : rien ne vous empêche de faire (au sein d'une méthode de classe) this.aMethod = ()=>{} . Les méthodes devraient-elles également être readonly pour empêcher de tels piratages ? idem pour delete this.aMethod

création

  • ne s'applique qu'à l'objet ou comme on parle ici de structure : class comme la classe définit les membres de l'objet, typescript empêche déjà implicitement de créer de nouvelles propriétés à la volée… par exemple this.unKnownProperty = "something" ts déclenchera une erreur de temps de compilation

extension

  • ne s'applique qu'aux classes, nous ne faisons encore rien sur l'objet lui-même… (contrairement à la création qui est à l'exécution). C'est là que final ou seal seraient pertinents. La question est de savoir comment rester cohérent avec le gel, le sceau et les preventExtensions de l'ES6 ? (s'il le faut).
    L'extension d'une classe signifie que nous empêchons de créer une autre classe à partir de cette classe. On peut alors le lire mais pas le supprimer (qui supprime la classe de toute façon). La question n'est pas la colonne "mise à jour". Une classe final -elle modifiable ? Qu'est-ce qu'une classe pouvant être mise à jour ? Je suppose que oui… mais alors la mise à jour serait d'implémenter cette classe (comprendre l'interface de la classe) ? Implémenter une autre classe devrait toujours être possible… mais ce n'est pas lié à la mise à jour de la classe initiale. Eh bien… peut-être qu'ils sont simplement venus aux mêmes questions en écrivant c# et ont décidé que seal était le bon mot-clé.

primordial

  • empêcher de remplacer la méthode actuelle dans une classe d'extension. Encore une fois, nous n'écrasons rien pour le moment car nous parlons d'une définition (méthode au sein d'une classe). Donc, empêcher la création, autoriser la lecture, autoriser la mise à jour (nous pourrions empêcher la mise à jour de la méthode ou la supprimer en utilisant la lecture seule si elle était également applicable aux méthodes). Meilleur nom ? preventOverrides ? ou encore seal tel qu'il est utilisé en C#

BONUS : Comme sceller une méthode c'est généralement empêcher les gens de passer outre le super code obligatoire, j'ai fait la proposition de forcer les gens à appeler la super méthode (le même comportement que pour le constructeur mais pour n'importe quelle méthode) n'hésitez pas à voter si vous pense que ce serait utile.

Ne vous méprenez pas, je ne suis pas contre. Je suis tout à fait pour. J'essaie juste de comprendre l'état d'esprit paradoxal de Microsoft sur les raisons pour lesquelles cela ne devrait pas être une fonctionnalité, qui consiste essentiellement à dire que, parce que ce n'est pas une fonctionnalité JavaScript, il ne peut pas s'agir d'une fonctionnalité TypeScript.

... Si je comprends bien.

@hk0i Les génériques, en lecture seule et de nombreuses autres fonctionnalités ne sont pas des fonctionnalités JavaScript, mais elles sont toujours ajoutées à TypeScript, donc je ne pense pas que ce soit la raison ...

TypeScript évite uniquement les fonctionnalités qui nécessitent de générer du code de manière non directe. Les génériques, en lecture seule, etc. sont des notions purement au moment de la compilation qui peuvent être simplement effacées pour produire la sortie générée, elles sont donc un jeu équitable.

final peut également être « effacé » au moment de la compilation, alors pourquoi cela n'en fait-il pas un « jeu équitable » ?

🤷‍♂️

@TheColorRed Je pense que c'est plus ou moins ce soit ils ont une autre priorité à intégrer.
Ou ils ne savent pas dans quelle mesure ce nouveau mot-clé pourrait fonctionner pendant la compilation/le temps de codage. si vous y réfléchissez, la conception du mot-clé final peut être extrêmement vague. final pourrait être utilisé beaucoup à de nombreuses fins. Comme vous pouvez le voir dans les documentations Jsdoc, vous pouvez utiliser la balise "@final" qui indique à l'interpréteur jsdoc que cette variable est gelée donc en lecture seule.

ici un choc de conception. Readonly est applicable sur la variable et le getter si je me souviens qui a "gelé" la variable et la rend en lecture seule et final la rend finale donc en lecture seule également.

cela peut être source de confusion pour le programmeur qui ne sait pas s'il doit étiqueter ses variables comme final ou en lecture seule.

À MOINS que nous réservions ce mot clé exclusivement pour les déclarations de classe et de méthode, je pense que le comportement ou la finale entrerait en conflit avec readonly.

Je pense qu'au lieu de scellé (qui peut entrer en conflit avec object.seal) ou final (ce qui a pour but de créer un conflit avec le mot-clé readonly), nous pourrions utiliser une manière plus "directe" de le nommer et de le concevoir.

prendre note ceci simplement une "idée" d'un comportement qui est similaire mais en même temps différent du comportement C# ? Je me suis inspiré de la façon dont C# est normalement non Overridable et ensuite vous devez dire que cette méthode est virtuelle.

` Exemple d'espace de noms {

export class myClass {

    constructor(){}

    // Make the func non ovveridable by inheritance. 
    public unoveridable myMethod(){

    }

    public myMethodB(){

    }
}

export class MyClassB extends myClass {

    // Nope not working will throw an error of an nonOverridable error
    myMethod(){
        super.myMethod();
    }

    // Nope will not work since unoverridable.
    myMethod(){

    }

    // Design could be implemented differently since this could complicate  ¸
    // the pattern of typescript
    public forceoverride myMethod(){
      // destroy previous pattern and ignore the parent method thus a true ovveride
    }

    // Yup will work since it's still "virtual".
    myMethodB(){
        super.myMethodB();
    }
}
// Can extend an existing Parent class
// Declaring an unextendable class make all is component / func nonOverridable by 
// inheritance. (A class component can still be overwritten by prototype usage.)
export unextendable class MyFinalClass extends Parent {

    constructor()

    // nonoverridable by default.
    public MyMethod(){

    }
}
// nope throw error that MyFinalClass is locked and cannot be extended
export class MyChildClass extends MyFinalClass{}

}`

Edit : je n'ai pas inclus Variable et get car readonly existe déjà.

@mhegazy Veuillez envisager de rouvrir ce problème. La réponse de la communauté semble être majoritairement du côté de l'ajout d'un mot-clé final , comme Java ( final ), C# ( sealed , readonly ), PHP ( final ), Scala ( final , sealed ) et C++ ( final , virtual ) ont. Je ne peux pas penser à un langage POO à typage statique qui n'ait pas cette fonctionnalité, et je n'ai pas entendu d'argument convaincant pour expliquer pourquoi TS n'en a pas besoin.

@bcherny @mhegazy Je suis tout à fait d'accord avec ce qui précède. Je ne vois aucune raison bien argumentée pour laquelle final ne peut pas être simplement une autre contrainte de compilation, comme la plupart du sucre syntaxique que nous voyons dans TypeScript - avouons-le, typage statique, génériques, modificateurs d'accès, alias de type , interfaces... TOUT SUCRE ! Je comprends que l'équipe TypeScript veut probablement se concentrer sur l'utilisation du langage dans d'autres directions, mais sérieusement, c'est la POO 101... cela aurait dû être pensé il y a longtemps, quand TypeScript était encore très jeune !

@mhegazy puis-je vous renvoyer à un premier commentaire que vous avez fait sur ce problème ?

une classe avec un constructeur privé n'est pas extensible. pensez à l'utiliser à la place.

Au moment de la rédaction, ce commentaire a été rejeté 21 fois.
Il est clair que ce n'est PAS CE QUE LA COMMUNAUTÉ VEUT COMME SOLUTION !

wow, j'ai eu beaucoup de retours à ce sujet. Je ne comprends pas pourquoi aucun intérêt à mettre en œuvre

Beaucoup de plaintes vraiment improductives se passent ici. S'il vous plaît, ne vous présentez pas uniquement pour exprimer votre frustration - nous sommes autorisés à prendre des décisions ici et la communauté est autorisée à donner son avis, mais un simple grincement de dents ne fera pas changer d'avis. Veuillez être précis et exploitable ; nous n'allons pas être convaincus par des gens qui viennent juste pour dire FAIRE CETTE FONCTIONNALITÉ MAINTENANT .

Notez que ce problème concerne les classes finales/scellées . Il y a eu des discussions hors sujet sur les méthodes finales/scellées qui sont un concept complètement différent.

Certains points ont été pris en compte lors de notre dernière revue

  • LES CLASSES SONT UNE FONCTIONNALITÉ JAVASCRIPT, PAS UNE FONCTION TYPESCRIPT . Si vous avez vraiment l'impression que les cours sont complètement inutiles sans final , c'est quelque chose à aborder avec le comité TC39 ou à apporter au es-discussion. Si personne ne veut ou ne peut convaincre TC39 que final est une fonctionnalité indispensable, alors nous pouvons envisager de l'ajouter à TypeScript.
  • S'il est légal d'invoquer new sur quelque chose, cela a une correspondance d'exécution un à un avec l'héritage de celui-ci. La notion de quelque chose qui peut être new 'd mais pas super 'd n'existe tout simplement pas en JavaScript
  • Nous faisons de notre mieux pour éviter de transformer TypeScript en une soupe de mots clés POO. Il existe de nombreuses meilleures mécaniques de composition que l'héritage en JavaScript et nous n'avons pas besoin d'avoir tous les mots-clés de C# et Java.
  • Vous pouvez trivialement écrire une vérification d'exécution pour détecter l'héritage d'une classe "devrait être finale", ce que vous devriez vraiment faire de toute façon si vous êtes "inquiet" que quelqu'un hérite accidentellement de votre classe car tous vos consommateurs n'utilisent peut-être pas TypeScript .
  • Vous pouvez écrire une règle de charpie pour appliquer une convention stylistique dont certaines classes ne sont pas héritées
  • Le scénario selon lequel quelqu'un hériterait « accidentellement » de votre classe qui devrait être scellée est en quelque sorte étrange. Je dirais également que l'évaluation individuelle de quelqu'un pour savoir s'il est ou non «possible» d'hériter d'une classe donnée est probablement plutôt suspecte.
  • Il y a essentiellement trois raisons de sceller une classe : la sécurité, les performances et l'intention. Deux d'entre eux n'ont pas de sens dans TypeScript, nous devons donc considérer la complexité par rapport au gain de quelque chose qui ne fait qu'un tiers du travail qu'il fait dans d'autres environnements d'exécution.

Vous êtes libre d'être en désaccord avec tout cela, mais veuillez apporter des commentaires concrets et concrets sur la façon dont le manque de final nuit à votre capacité à utiliser efficacement les classes JavaScript.

Je n'ai pas entendu d'argument convaincant expliquant pourquoi TS n'en a pas besoin

Juste un rappel que ce n'est pas comme ça que ça marche. Toutes les fonctionnalités commencent à -100 . De ce poste

Dans certaines de ces questions, les questions demandent pourquoi nous avons « retiré » ou « laissé de côté » une caractéristique spécifique. Cette formulation implique que nous avons commencé avec un langage existant (C++ et Java sont les choix populaires ici), puis que nous avons commencé à supprimer des fonctionnalités jusqu'à ce que nous arrivions à un point qui nous plaisait. Et, bien que cela puisse être difficile à croire pour certains, ce n'est pas ainsi que le langage a été conçu.

Nous avons un budget de complexité finie. Tout ce que nous faisons ralentit de 0,1 % chaque étape suivante. Lorsque vous demandez une fonctionnalité, vous demandez que toutes les autres fonctionnalités deviennent un peu plus difficiles, un peu plus lentes, un peu moins possibles. Rien n'entre ici parce que Java l'a ou parce que C# l'a ou parce que ce ne serait que quelques lignes de code ou vous pouvez ajouter un commutateur de ligne de commande . Les fonctionnalités doivent payer pour elles-mêmes et pour tout leur fardeau sur l'avenir.

@RyanCavanaugh Bien que je ne sois pas sûr d'être d'accord avec la décision, j'apprécie la réponse vraiment réfléchie et détaillée, et le fait que vous

@RyanCavanaugh

Je ne pense pas que le point ici, dans ce forum est .. "utiliser efficacement les classes JavaScript."

Désolé, je ne peux pas résister, et ajouter juste à la "plainte improductive qui se passe ici": le mot-clé final est utile pour toutes les raisons expliquées, en détail et avec des arguments par d'autres.

Nous faisons de notre mieux pour éviter de transformer TypeScript en une soupe de mots clés POO. Il existe de nombreuses meilleures mécaniques de composition que l'héritage en JavaScript...

Comme lorsque vous utilisez final pour appliquer la composition sur l'héritage (insérez pas le nom du langage dactylographié ici) 😈
(Désolé, je n'ai pas pu résister)

Mais plus précisément, il semble que TS ait principalement l'intention d'être une couche de compatibilité pour introduire "le JavaScript d'aujourd'hui maintenant" ou quelque chose dans ce sens (à la Babel) à l'exception du fait qu'il ajoute une vérification de type supplémentaire.

Je pense que l'essentiel du contre-argument pour implémenter ceci est que si vous voulez les fonctionnalités d'une autre langue, utilisez l'autre langue à la place. Si vous voulez du JavaScript, utilisez JavaScript. Si vous voulez du TypeScript, utilisez-le.

Je sais que Kotlin peut compiler en JS et possède de nombreuses fonctionnalités que je pense que les gens ici apprécieraient, c'est donc quelque chose qui pourrait être utile à examiner. Je pense qu'il ajoute cependant une bibliothèque JS en tant que dépendance (je ne l'ai pas essayé moi-même).

Le principal point à retenir est que si vous n'êtes pas satisfait de TypeScript, ne l'utilisez pas.

@RyanCavanaugh

Sur les points que vous avez soulevés ci-dessus...

LES CLASSES SONT UNE FONCTIONNALITÉ JAVASCRIPT, PAS UNE FONCTION TYPESCRIPT . Si vous avez vraiment l'impression que les cours sont complètement inutiles sans final , c'est quelque chose à aborder avec le comité TC39 ou à apporter au es-discussion. Si personne ne veut ou ne peut convaincre TC39 que final est une fonctionnalité indispensable, alors nous pouvons envisager de l'ajouter à TypeScript.

Je suis TypeScript depuis la 0.7 et à l'époque, IIRC ciblait ES3-ES5. Les classes étaient définitivement une fonctionnalité TypeScript à l'époque. Le même argument s'applique aux décorateurs - ils sont sur les cartes pour ES, mais ils sont déjà là dans TypeScript en tant que fonctionnalité expérimentale.

En tant que communauté, nous ne prétendons pas que le compilateur TypeScript doit en quelque sorte émettre une classe JavaScript qui est final . Un contrôle au moment de la compilation suffirait, de la même manière que les contrôles au moment de la compilation pour les modificateurs d'accès suffisent ; après tout, ce ne sont pas non plus des fonctionnalités JavaScript, mais TypeScript les rend toujours possibles.

Si final devenait une fonctionnalité ES, alors vous pourriez vraisemblablement refactoriser ce bit de l'émetteur en ciblant ES* pour produire final manière appropriée (de la même manière que vous l'avez fait pour les classes, quand elles ont été introduites dans ES6) ?

S'il est légal d'invoquer new sur quelque chose, cela a une correspondance d'exécution un à un avec l'héritage de celui-ci. La notion de quelque chose qui peut être new 'd mais pas super 'd n'existe tout simplement pas en JavaScript

Encore une fois, je ne pense pas que la communauté s'attend à ce que vous fassiez quelque chose ici pour imposer final au moment de l'exécution. Comme vous l'avez dit avec précision, cela "n'existe pas en JavaScript", mais comme mentionné, une contrainte de compilation suffirait.

Nous faisons de notre mieux pour éviter de transformer TypeScript en une soupe de mots clés POO. Il existe de nombreuses meilleures mécaniques de composition que l'héritage en JavaScript et nous n'avons pas besoin d'avoir tous les mots-clés de C# et Java.

C'est une phrase assez bien connue dans l'ensemble de la communauté du développement de logiciels : « Favoriser la composition à l'héritage », cependant « favoriser » ne signifie pas ne pas utiliser l'héritage comme une déclaration générale. Bien sûr, vous n'avez pas besoin de tous les mots clés de C# et Java, mais vous devez avoir une justification pour autoriser les classes abstract (qui d'ailleurs "n'existent pas en JavaScript"), alors pourquoi pas final cours ?

Vous pouvez trivialement écrire une vérification d'exécution pour détecter l'héritage d'une classe "devrait être finale", ce que vous devriez vraiment faire de toute façon si vous êtes "inquiet" que quelqu'un hérite accidentellement de votre classe car tous vos consommateurs n'utilisent peut-être pas TypeScript .

La même chose pourrait être dite pour C# et Java, mais je n'ai pas besoin de vérifications à l'exécution car j'ai sealed et final pour le faire pour moi au niveau du compilateur.

Si tous mes consommateurs n'utilisaient pas TypeScript, j'aurais beaucoup plus à craindre que le simple héritage d'une classe final ; par exemple, l'accès aux membres private ou protected , aucune vérification de type statique, ou la possibilité de construire une classe abstract .

Notre code devrait-il vraiment être jonché de vérifications d'exécution pour toutes ces choses (dont certaines ne sont même pas possibles, je pourrais ajouter) juste pour s'assurer que nos consommateurs non-TypeScript sont aussi sûrs que possible ?

Vous pouvez écrire une règle de charpie pour appliquer une convention stylistique dont certaines classes ne sont pas héritées

Cela impliquerait que celui qui consomme votre code exécute le linter, donc ce n'est toujours pas vraiment une bonne solution ; c'est une solution de contournement.

Le scénario selon lequel quelqu'un hériterait « accidentellement » de votre classe qui devrait être scellée est en quelque sorte étrange. Je dirais également que l'évaluation individuelle de quelqu'un pour savoir s'il est ou non «possible» d'hériter d'une classe donnée est probablement plutôt suspecte.

Les bons/grands développeurs réfléchissent à la raison pour laquelle ils font quelque chose, comme étendre une classe. Pas moins que ça et vous vous retrouvez avec une mentalité « mais ça marche », sans vraiment comprendre pourquoi.

Poser une question qui commence par puis-je a généralement une réponse plus définitive que les questions qui commencent par devrais-je .

Il y a essentiellement trois raisons de sceller une classe : la sécurité, les performances et l'intention. Deux d'entre eux n'ont pas de sens dans TypeScript, nous devons donc considérer la complexité par rapport au gain de quelque chose qui ne fait qu'un tiers du travail qu'il fait dans d'autres environnements d'exécution.

Je dirais sans doute que TypeScript remplirait en fait deux de ces raisons : la sécurité et l'intention, mais si cela était implémenté uniquement en tant que vérification du temps de compilation, il ne pourrait le faire qu'au niveau du compilateur, plutôt que, comme vous le faites remarquer à juste titre , au niveau de l'exécution.

Je ne pense pas que le fait de ne pas avoir final nuit à ma capacité à utiliser efficacement les classes JavaScript, et les classes JavaScript ne sont pas inutilisables sans cela. Je pense que final est plus du côté d'une fonctionnalité "agréable à avoir", plutôt que d'une fonctionnalité "nécessaire", mais on pourrait en dire autant pour de nombreuses fonctionnalités de TypeScript.

Je pense que le principal reproche ici est que TypeScript nous permet de créer des classes abstract et _open_, mais en termes d'héritage et de polymorphisme, il ne nous permet pas de brosser un tableau complet de manière élégante et expressive.

Cacher le constructeur n'est pas ce que la communauté veut parce que cela enlève la valeur sémantique et expressive des implémentations de classe, et encore une fois, parce que les modificateurs d'accès sont un "agréable à avoir", cela ne peut même pas être appliqué au moment de l'exécution ; essentiellement une situation "n'existe pas en JavaScript".

En tant que développeur, j'ai plusieurs casquettes ; en ce moment j'ai un chapeau C#, un chapeau Kotlin et un chapeau TypeScript.

  • Avec mon chapeau Kotlin, je n'ai pas à me soucier des final car les cours sont définitifs par défaut.
  • Avec mon chapeau C#, j'applique sealed par habitude, forçant mes consommateurs à demander puis-je plutôt que de demander si je . Le plus souvent, les classes C# devraient en fait être sealed et c'est souvent négligé.
  • Avec mon chapeau TypeScript, je dois cacher le constructeur comme solution de contournement, car à mon avis (et à celui des communautés), il manque quelque chose d'utile au langage.

Par intérêt, lorsque l'équipe TypeScript s'est assise autour d'une table et a discuté des cours de abstract , est-ce que quelqu'un a levé la main et a dit « mais qu'en est-il de final » ?

@RyanCavanaugh Hum, je comprends vos points

Je pense que c'était surtout un malentendu (je suppose d'après la façon dont nous avons expliqué)

@series0ne résume très bien que nos intentions n'étaient pas trop compliquer la sortie dactylographiée en demandant de créer la classe finale dans JS (bon sang, ce serait une épave de train pour le faire) mais juste de créer des règles supplémentaires pour dire aux gens : Non, tu peux ' fais pas ça. et c'est dire à l'utilisateur de l'API qu'il ne devrait probablement pas jouer avec ces classes (pour des raisons de stabilité, de sécurité, etc.) TOUT sur les fichiers/compilateur tapuscrit PAS sur la sortie.

Est-ce que je pense que ce serait bien d'avoir cette fonctionnalité : oui c'est bien d'avoir un moyen plus rapide de faire de la technique.

Est-ce VRAIMENT nécessaire ? Pas vraiment, cela pourrait être quelque chose de mineur et ne devrait pas être une priorité si l'équipe Typescript peut se permettre d'implémenter ce supplément, alors cela pourrait être bien d'avoir.

Je vois que le mot-clé final/scellé pourrait compliquer BEAUCOUP certains aspects en particulier : Correction de bug, Patch, changement de comportement.

si j'explique cela, je pourrais totalement voir une utilisation abusive de final en empêchant TOUTES les classes d'être étendues. Provoquer l'OMI une grande douleur dans le a ** à traiter. Je ne veux pas utiliser une API où je suis bloqué pour modifier la classe d'utilisateurs par héritage car ils étaient paranoïaques à cause de la "sécurité".

Cela signifierait que je devrais utiliser prototype puis alias ou remplacer cette classe juste pour pouvoir modifier le comportement de cette classe pour l'adapter à mes besoins.
ce serait ennuyeux, surtout si je veux juste ajouter des fonctionnalités supplémentaires à ladite classe et que je ne veux pas utiliser un flux de travail destructeur.

Après cela, je pense que je pourrais mal comprendre les situations en ce moment, alors soyez libre de me l'expliquer :)

Ah si je me souviens bien, j'ai lu beaucoup de choses sur l'ES4 (je ne suis pas sûr d'être encore un gamin quand le brouillon était en production lol) et étonnamment, ça ressemble vraiment à Typescript.

et c'est dit dans le projet de cahier des charges ici :
https://www.ecma-international.org/activities/Languages/Language%20overview.pdf

A class that does not explicitly extend any other class is said to extend the class Object. As a consequence, all the classes that exist form an inheritance hierarchy that is a tree, the root of which is Object. A class designated as final cannot be extended, and final methods cannot be overridden: class C { final function f(n) { return n*2 } } final class D extends C { function g() { return 37 } }

Donc, techniquement, le mot-clé final a été prévu dans la 4e édition d'Ecmascript. Le verra-t-on dans les futures intégrations de JS j'en doute mais ils ont bel et bien intégré la classe donc c'est peut-être possible mais qui sait ?

J'ai un cas d'utilisation que j'ai à peine rencontré qui m'a conduit à trouver ce problème. Il tourne autour de l'utilisation de this comme type de retour :

abstract class TileEntity {
    //constructor, other methods...
    abstract cloneAt(x: number, y: number): this;
}

Mon intention en écrivant ceci était de m'assurer que les TileEntities sont immuables (et donc sûrs à circuler) mais que n'importe quelle TileEntity donnée pourrait être clonée avec quelques propriétés modifiées (dans ce cas x et y ) sans avoir à connaître quoi que ce soit sur la forme de la sous-classe. C'est ce que je veux écrire, mais essayer d'écrire une implémentation (correctement) provoque la rupture.

class TurretTileEntity extends TileEntity {
    //constructor, other methods...
    cloneAt(x: number, y: number): this {
        return new TurretTileEntity(x, y, /* ... other properties */);
    }
}

Le problème est que sans pouvoir déclarer qu'il n'y aura _jamais_ de sous-classe de TurretTileEntity, le type de retour this ne peut pas être réduit au seul type TurretTileEntity.

L'impact de cette lacune dans Typescript est minime, étant donné que vous pouvez convertir la valeur de retour en <any> . C'est, objectivement, un anti-modèle et une indication utile que le système de types pourrait être plus expressif. Les classes finales/scellées devraient être prises en charge.

Par intérêt, lorsque l'équipe TypeScript s'est assise autour d'une table et a discuté des cours abstraits, est-ce que quelqu'un a levé la main et a dit « mais qu'en est-il du final » ?

Oui. Chaque fois que nous ajoutons un mot-clé, "mais alors nous devrons aussi ajouter cet autre mot-clé !" est un point fort contre son ajout, car nous voulons développer la langue aussi soigneusement que possible. final sur les déclarations de classe est également dans ce camp - si nous avons des classes final , alors nous aurons également "besoin" de final méthodes ou propriétés. Tout ce qui ressemble à une dérive de la portée est considéré avec une extrême méfiance.

Je pense que les classes et les méthodes finales sont très importantes. J'espère que vous l'appliquerez.

Notez que ce problème concerne les classes finales/scellées. Il y a eu des discussions hors sujet sur les méthodes finales/scellées qui sont un concept complètement différent.

mhegazy a fermé https://github.com/Microsoft/TypeScript/issues/9264 en faveur de ce problème. Existe-t-il un problème distinct qui n'est pas clos en tant que dupe pour suivre les méthodes finales/scellées ?

@crcla En lisant certains des commentaires de

Je ne peux pas croire que la proposition pour final soit étiquetée comme Won't fix .

Remplacez l'étiquette par In Discussion et implémentez-la, s'il vous plaît !

Dans mon cas, je veux écrire une classe de base abstraite pour les plugins et je veux m'assurer que la classe du plugin ne peut pas écraser mes méthodes de classe de base par erreur. Aucun des private constructor , Object.seal ne peut faire ce travail.

Pour ceux qui voient cela comme une fonctionnalité cruciale, vivre ou mourir : ce n'est vraiment pas le cas. Si votre API consiste à exposer une implémentation de classe qui doit être étendue par le consommateur, il existe de meilleures façons de le faire. Ne suivez pas le mauvais exemple de React. Utilisez simplement des fonctions et des objets simples, et laissez ce non-sens OO à sa place : dans le passé.

Maintenant amenez les votes négatifs 😀

Quelqu'un pourrait-il m'expliquer en quoi cette discussion ne fait pas de distinction entre les classes finales et les méthodes finales? Bien sûr, empêcher certaines méthodes de se substituer ajoute une valeur énorme. Je veux donner la possibilité à un développeur de plugins d'ajouter des fonctionnalités en sous-classant certaines de mes classes de frameworks et en implémentant des fonctions abstraites - mais en même temps, assurez-vous qu'elle ne remplace pas (pourquoi jamais !) les méthodes qui appellent ces fonctions abstraites (et assurez-vous que la gestion/la connexion/la vérification des erreurs ont lieu.

@pelotom Si vous ne voulez pas utiliser de constructions et de modèles POO, ne le faites pas, personne ne vous dit de faire autrement, mais la même chose devrait s'appliquer dans la direction opposée : si quelqu'un veut utiliser des constructions POO, laissez-le simplement.
En forçant les autres à adopter un modèle spécifique (non POO), vous adoptez le même comportement que celui pour lequel vous dénoncez la POO. Soyez au moins cohérent :)

L'ajout d'un mot-clé final à TypeScript n'est pas un changement radical et n'influencera aucun de vos projets existants, mais ce sera une fonctionnalité vitale pour des personnes telles que @thorek , moi-même et bien d'autres.

si quelqu'un veut utiliser des constructions POO, laissez-le simplement.

Nous ne débattons pas pour savoir si les gens devraient utiliser des constructions OOP, nous débattons si des constructions OOP actuellement inexistantes devraient être ajoutées au langage. Cela prend du temps au développeur qui pourrait être consacré à d'autres fonctionnalités et rend le langage plus complexe, ce qui ralentit le rythme des fonctionnalités futures (car pour chaque nouvelle fonctionnalité, il faut considérer comment il interagira avec chaque ancienne fonctionnalité). Cela m'affecte donc certainement, car je préférerais dépenser le budget de développement et de complexité de TypeScript pour des choses plus utiles !

fwiw, nous allons probablement encourager nos clients (google) à utiliser @final dans les commentaires afin qu'il se propage au compilateur de fermeture :\

L'ajout de _final_ sur les classes et les méthodes est utile lorsqu'une équipe de R&D crée des bibliothèques TS que d'autres équipes peuvent utiliser. Ces mots-clés sont une partie (importante) de la stabilité de l'exposition au service de ces outils.
Le mot-clé est plus important pour les grandes équipes et moins important pour les petits projets, à mon humble avis.

@dimiVergos C'est exactement mon cas, et je suppose la même chose pour beaucoup d'autres. Je peux imaginer que je peux sembler biaisé en raison de ma position, mais cela semble la seule utilisation (bien que hautement) justifiable de ce mot-clé selon mon expérience.

Je suis ouvert aux corrections sur celui-ci !

Je me soucie plus de la prise en charge des méthodes finales (pour éviter que les sous-classes ne les remplacent accidentellement) que des classes finales. Est-ce le bon ticket pour voter pour ça ? Ticket https://github.com/Microsoft/TypeScript/issues/9264 semble l'impliquer.

Alors comment voter pour les « méthodes finales » ? Ou est-ce que je viens de voter pour lui par ce commentaire ? De plus, comment ajouter des « méthodes » au titre ? Puis-je simplement le modifier ? Le titre actuel est limité de manière trompeuse aux seules classes, mais la discussion inclut également les méthodes.

Comme beaucoup d'autres, j'aimerais voir les méthodes finales dans TypeScript. J'ai utilisé l'approche suivante comme solution de contournement partielle qui n'est pas à l'abri de l'échec mais qui aide au moins un peu :

class parent {
  public get finalMethod(): () => void {
    return () => {
      // final logic
    };
  }
}

class child extends parent {
  // ERROR "Class 'parent' defines instance member accessor 'finalMethod', but extended class 'child' defines it as instance member function"
  public finalMethod(): void { }
}

Voici un autre cas d'utilisation réel pour les méthodes final . Considérez l'extension suivante d'un React.Component pour faciliter l'implémentation d'un Context simple .

interface FooStore{
  foo: string;
}

const {Provider, Consumer} = React.createContext<FooStore>({
  foo: 'bar';
});

abstract class FooConsumerComponent<P, S> extends React.Component<P, S> {

  // implement this instead of render
  public abstract renderWithStore(store: FooStore): JSX.Element;

  public final render() {
    return (
      <Consumer>
        {
          (store) => {
            return this.renderWithStore(store);
          }
        }
      </Consumer>
    );
  }
}

Sans final sur la méthode render() , rien n'empêche de surcharger incorrectement la méthode, brisant ainsi tout.

Salut, comme beaucoup d'autres, j'aimerais au moins un mot-clé "final" ou "scellé" pour les méthodes. Je suis tout à fait d'accord pour dire que ce mot clé est une vraie valeur ajoutée car il permet aux développeurs d'empêcher leurs clients de librairies d'étendre des blocs de code cruciaux (comme dans le plugin par exemple).

La seule solution de contournement que j'ai trouvée est d'utiliser un décorateur de méthode comme :
function sealed(target, key,descriptor){ descriptor.writable = false; // Prevent method to be overriden }

puis dans votre classe "base", utilisez decorator pour empêcher le remplacement de méthode, exemple :

class MyBaseClass {
<strong i="10">@sealed</strong>
public MySealedMethod(){}

public OtherMethod(){}

...
}

De cette façon, 'MySealedMethod' ne pouvait pas (facilement) être remplacé dans la sous-classe. Exemple :

class AnotherClass extends MyBaseClass {
public MySealedMethod(){} ====>> ERROR at RUNTIME (not at compile time)
}

en fait, le seul inconvénient de cette solution de contournement est que le "scellé" n'est visible qu'à l'exécution (erreur dans la console javascript) mais PAS au moment de la compilation (car les propriétés de la méthode sont définies via la fonction, je suppose)

@RyanCavanaugh

Oui. Chaque fois que nous ajoutons un mot-clé, "mais alors nous devrons aussi ajouter cet autre mot-clé !" est un point fort contre son ajout, car nous voulons développer la langue aussi soigneusement que possible.

Il me semble que vous commencez cet argument avec votre esprit contre la fonctionnalité. Une fonctionnalité populaire ne devrait pas être un point contre la considération d'une telle fonctionnalité. Cela devrait être un point pour vous demander _pourquoi c'est populaire_, puis l'examiner attentivement.

Je suis sûr que vous en avez discuté et que des arguments ont été lancés pour l'envisager ou contre. L'argument contre cela est-il simplement qu'il s'agit d'un fluage de fonctionnalités, ou y a-t-il quelque chose de plus spécifique ? Considérez-vous cela comme une fonction marginale ? Si oui, pourrions-nous faire quelque chose pour vous faire changer d'avis ?

Permettez-moi de laisser ces questions ici pour que nous réfléchissions tous.

Existe-t-il un moyen de sceller une classe _par conception_ en utilisant les constructions actuelles ?
Si la fonctionnalité était ajoutée, cela nous permettrait-il de faire quelque chose de nouveau ? (Comme dans, quelque chose que les constructions présentes ne permettent pas)
Si la fonctionnalité nous a permis de faire quelque chose de nouveau, est-ce quelque chose de précieux ?
L'ajout de classes scellées augmenterait-il trop la complexité du langage ?
L'ajout de classes scellées augmenterait-il trop la complexité du compilateur ?

Dans le but d'en discuter, je n'inclus pas les méthodes scellées dans la discussion et je n'inclus pas non plus les contrôles d'exécution dans l'implémentation. Nous comprenons tous que nous ne pouvons pas appliquer cela sur Javascript.

Pour info : je serais génial si le contre-pied de cette fonctionnalité était un peu plus que de simples déclarations générales contre le fluage des fonctionnalités.

@asimon Je pense que tout ce que vous avez dit a déjà été abordé par ce commentaire (vous devrez développer la section "afficher plus" pour le voir) et celui qui le suit.

Qu'est-ce qui changerait d'avis ici ? Les personnes se présentant avec des exemples de code qui n'ont pas fonctionné comme il aurait dû parce que le mot-clé sealed était manquant .

Le scénario auquel le mot-clé tente de répondre est celui où quelqu'un hérite par erreur d'une classe. Comment hériter par erreur d'une classe ? Ce n'est pas une erreur facile à faire. Les classes JavaScript sont intrinsèquement suffisamment fragiles pour que toute sous-classe doive comprendre les exigences comportementales de sa classe de base, ce qui signifie écrire une forme de documentation, et la première ligne de cette documentation peut simplement être "Ne pas sous-classer cette classe". Ou il peut y avoir un contrôle d'exécution. Ou le constructeur de classe peut être caché derrière une méthode de fabrique. Ou tout autre nombre d'options.

Pourquoi TypeScript doit-il avoir ce mot-clé alors que la situation en est déjà une dans laquelle vous auriez dû rencontrer au moins deux barrages routiers distincts vous disant de ne pas être là pour commencer ?

Je programme depuis environ une éternité maintenant et je n'ai pas une seule fois essayé de sous-classer quelque chose pour découvrir qu'il était scellé et que je n'aurais pas dû essayer. Ce n'est pas parce que je suis un super génie ! C'est juste une erreur extrêmement rare à faire, et en JavaScript, ces situations sont déjà celles où vous devez en premier lieu poser des questions à la classe de base. Alors quel problème est en train d'être résolu ?

Un cas réel que j'utiliserais final serait d'éviter de remplacer une méthode par quelque chose qui n'appellerait jamais la super méthode. Une méthode publique bien sûr. Mais la vraie solution que j'ai trouvée serait la possibilité de forcer n'importe quelle sous-méthode à appeler la super. https://github.com/Microsoft/TypeScript/issues/21388

Comme je ne suis pas un concepteur de langage, je ne peux pas parler des inconvénients de l'ajout d'un mot-clé au langage. Je dirai que je travaille actuellement sur une superclasse abstraite qui a une méthode que j'ai l'intention d'être finale, mais à mon grand désarroi, j'ai découvert que je ne pouvais pas le faire. Pour l'instant, je vais utiliser l'approche moins qu'idéale consistant à mettre un commentaire, mais les membres de l'équipe mettant en œuvre la classe pourraient ne pas lire ledit commentaire.

Je pense que ci-dessus, il y a beaucoup d'excellents exemples de quand/où final serait non seulement utile, mais fournirait une _valeur réelle_ et une _sécurité réelle_ à la plate-forme en cours de construction. Ce n'est pas différent pour moi. Comme je l'ai dit, je ne connais pas grand-chose aux complexités de la conception des langages, mais c'est clairement une fonctionnalité que les gens veulent/utiliseront/je ne vois pas vraiment comment en abuser ou que cela générerait du mauvais code. Je comprends le concept de craindre le fluage de portée, mais les développeurs TS n'ont pas à implémenter tout ce que la communauté veut.

Juste pour ajouter ma part au débat, je voudrais mettre mon pouce du côté du oui de l'échelle car ce numéro regorge de bons arguments soutenant l'ajout de la fonctionnalité, mais les arguments contre que je vois sont :

  • Cela pourrait conduire à un fluage de la portée (cela est gérable)
  • Les gens demanderont d'autres fonctionnalités (d'accord, mais peu importe, cela fait partie de la conception d'un langage et ce n'est pas un précédent juridique que nous créons)
  • Cela pourrait réduire l'efficacité (j'imagine que puisque l'équipe TS est pleine de mauvais culs, vous trouverez probablement un moyen de rendre cela gérable)
  • Commentaires opiniâtres « N'utilisez pas les concepts de la POO »

Aucun de ce qui précède ne semble être une menace crédible pour l'écosystème TS lui-même, ni le code qui en est produit ; ils semblent tous politiques (sauf peut-être celui de l'efficacité, mais j'ai une énorme confiance en vous les gars, donc je ne suis pas vraiment inquiet à ce sujet, et une diminution mineure de l'efficacité vaudrait la peine de ne pas faire exploser des constructions de composants / bibliothèques importantes par des erreurs d'héritage involontaires) .

J'espère que c'est constructif ! J'adore TS et je veux le voir continuer à s'améliorer !

??

--Éditer--

En réponse à @RyanCavanaugh :

So what problem is being solved?

Le problème en cours de résolution, je pense, est décrit par de nombreux commentaires ci-dessus. Je ne veux pas dire cela comme une insulte, mais cette question me donne l'impression que vous ne faites pas vraiment attention aux arguments présentés et que vous avez l'esprit tourné vers "non". Je ne veux pas manquer de respect avec cela, c'est juste que, honnêtement, ce fil est plein d'exemples de problèmes que cela résoudrait.

D'une manière ou d'une autre, j'ai raté ce commentaire , qui décrit très bien une préoccupation sérieuse pour l'ajout de la fonctionnalité et qui a du sens pour moi. Encore une fois, je ne connais pas la complexité de l'ajouter mais les problèmes d'efficacité mis à part (puisqu'il n'appartient pas aux développeurs de s'inquiéter de l'efficacité de TS, c'est le travail de l'équipe TS), il ne semble pas y avoir de bons arguments contre final (à mon humble avis).

La plupart des discussions récentes ont été centrées sur les méthodes finales (moins sur les classes finales). Les méthodes finales sont certainement mon intérêt principal. Je viens de remarquer que si vous développez le lien "ce commentaire" dans le post précédent de @RyanCavanaugh , il considère la discussion finale sur la méthode hors sujet. Cependant @mhegazy a suggéré que ce ticket est au bon endroit (lors de la fermeture du #9264). Donc, en tant qu'utilisateur final modeste, je suis perplexe quant à l'endroit où je devrais demander/voter pour les méthodes finales. Des conseils seraient appréciés.

@cbutterfield
La contradiction est quelque chose que j'ai également remarqué mais que je n'ai pas souligné dans mon message par peur de divaguer (j'ai en fait trouvé CE fil via #9264 et je suis venu ici @mhegazy ).
@RyanCavanaugh
@mhegazy
Selon le commentaire ci-dessus, où _est_ le bon endroit pour discuter des méthodes finales, car c'est en fait la chose qui m'intéresse le plus.

En repensant à mon commentaire fortement déclassé (assurez-vous d'aller ajouter votre 👎 si vous ne l'avez pas encore fait !), Je suis heureux d'annoncer que React _ne donne plus_ un mauvais exemple en forçant une API basée sur class sur ses utilisateurs ! Avec la proposition Hooks, l'équipe React a repris ses esprits et a adopté une approche fonctionnelle radicalement plus simple. Avec cela, la dernière raison pour laquelle de nombreux développeurs doivent utiliser des classes s'est évaporée, et bon débarras. Je le répète : tout ce pour quoi vous pensez avoir besoin de cours peut être mieux fait sans eux .

L'avenir de JS et TS est sans classe, louez-le !

@cbutterfield J'ai remarqué cela aussi, et si le numéro 9264 était fermé pour être discuté dans ce numéro, je pense que le titre devrait changer en Suggestion: Final keyword for classes and methods , sinon les personnes à la recherche de méthodes finales (exclusivement) pourraient ne pas ajouter de réaction dans le premier poste.

Citant @mhegazy :
Java et/ou C# utilise la classe final pour optimiser votre classe à l'exécution, sachant qu'elle ne sera pas spécialisée. je dirais que c'est la principale valeur du support final. Dans TypeScript, nous ne pouvons rien offrir pour que votre code s'exécute mieux qu'il ne l'a fait sans final.

Je suis fondamentalement en désaccord. J'ai développé en Java pendant de nombreuses années dans de nombreuses équipes et nous n'avons jamais utilisé de classes ou de méthodes finales pour les optimisations d'exécution ; en pratique, il s'agit principalement d'un idiome de conception POO. Parfois, je ne veux tout simplement pas que ma classe ou ma méthode soit étendue/surchargée par des sous-classes externes, que ce soit pour protéger et garantir une fonction, ou pour limiter le bruit d'API d'une bibliothèque à l'essentiel, ou pour protéger les utilisateurs d'API d'une mauvaise compréhension d'une API complexe.

Oui, ces problèmes peuvent être résolus en ajoutant des commentaires ou des interfaces, mais final est une solution élégante pour réduire l'API visible aux fonctions nécessaires lorsque tout ce que vous voulez est une classe simple avec une API propre et extensible.

Même si ce problème date de près de 3 ans, nous devrions vraiment avoir un mot-clé final car il existe déjà un mot-clé abstract .

Gardez à l'esprit que nous ne voulons pas forcer les gens à l'utiliser et c'est une fonctionnalité aussi utile que le mot-clé abstrait. Mais voici un autre cas d'utilisation qui montrera fortement l'avantage d'un mot-clé final :

abstract class A {
  protected abstract myVar: string;

  protected abstract myFunction(): void;
}

class B extends A {
  protected readonly myVar: string = "toto";

  constructor() {
    super();
    this.myFunction();
  }

  protected myFunction() {
    console.log(this.myVar);
  }
}

class C extends B {
  constructor() {
    super();
  }

  protected myFunction() {
    console.log("tata");
  };

  public callFunction = () => {
    this.myFunction();
  }
}

const myB = new B(); // toto

const myC = new C(); // toto
myC.callFunction(); // tata

Résultat après compilation :

toto
tata

Donc, dans ce code, nous avons une classe abstraite A qui a des méthodes et des propriétés abstraites, nous voulons que la classe héritée les définisse, mais nous aimerions ne laisser aucune autre classe remplacer ces implémentations.
Le mieux que nous puissions faire serait de conserver le mot-clé protected , mais comme vous le voyez, nous pouvons toujours redéfinir les attributs.

Et si nous allions plus loin dans le processus de compilation Typescript et utilisions le readonly pour protéger nos attributs (et prétendre que nos méthodes sont des propriétés) ?

class B extends A {
  [...]

  protected readonly myFunction = () => {
    console.log(this.myVar);
  }
}

class C extends B {
  protected myVar: string = "I am not protected that much";
  [...]

  protected myFunction = () => { // using the readonly keyword doesn't prevent any modification
    console.log("tata"); // If you launch this code, tata will still be logged in the console.
  };
}

(Notez que le code est compilé avec tsc, aucune erreur n'est renvoyée lors de la compilation ou de l'exécution)
Alors maintenant, nous avons deux problèmes :

  • Nous ne protégeons pas nos attributs B , nous avons donc besoin d'un moyen d'empêcher tout héritage inattendu.
  • Maintenant, nous savons que nous pouvons remplacer toutes les propriétés readonly en étendant sa classe, et OUI, Typescript sait que c'est la propriété parent que nous modifions en utilisant private au lieu de protected dans la classe C renverra une erreur car ce n'est pas le même type d'accès/visibilité.

Pour le moment, nous pourrions utiliser des décorateurs ( scellés comme le montre la documentation officielle de Typescript, ou un décorateur final ) mais rappelez-vous que cela n'est utile qu'à l'exécution, et que nous devons plutôt empêcher cela dans le processus de compilation.

Je vais regarder s'il y a un problème concernant la "violation de sécurité" (si nous pouvons appeler cela) lorsque nous voulons remplacer une valeur en lecture seule protégée et que nous pouvons et nous ne devrions pas le faire. Sinon j'ouvrirai un topic pour débattre de la vérification du mot-clé readonly parmi private , protected mot-clé avec des trucs d'héritage.

tl ; dr : Le mot-clé final résoudrait deux problèmes (bien que la vérification readonly soit un bon début pour empêcher toute réécriture non autorisée)

Trois ans plus tard... c'est tellement ridicule.

Afin de prouver la nécessité de cette fonctionnalité, je suggère de jeter un œil à ce qui a été fait dans d'autres langues :

Java:
final class SomeClass { ... }
PHP:
final class SomeClass { ... }
C#:
sealed class SomeClass {...}
C++:
class SomeClass final { ... }
Delphi:
type SomeClass = class sealed() ... end;

Ainsi, nous voyons que dans tous les langages POO les plus populaires, cette fonctionnalité existe car elle provient de la logique d'héritage de la POO.

Je dois également citer le responsable du développement de TS en disant

si nous avons des classes finales, nous aurons également " besoin " de méthodes ou de propriétés finales.

Je ne sais pas si c'est ironique, mais dans JS, le mot-clé readonly est utilisé pour les propriétés (et les méthodes si vous trichez), donc c'est un point assez stupide.

Et peut-être que ne pas laisser un gars clore le problème alors qu'il a +40 votes négatifs, ce qui signifie que la communauté est en profond désaccord avec lui serait un bon conseil pour éviter d'autres situations stupides.

Si vous n'êtes pas intéressé à renforcer la fonctionnalité principale de Typescript, dites-le fort afin que les nouvelles techniques puissent le relayer sur Twitter, car sinon, ce n'est qu'un bad buzz caché. Vous ignorez simplement votre communauté ET vous n'avez aucun processus pour que la communauté valide ou non ce dont nous avons besoin.

peut-être que ne pas laisser un gars clore le problème lorsqu'il a + 40 votes négatifs, ce qui signifie que la communauté n'est pas du tout d'accord avec lui serait un bon conseil pour éviter d'autres situations stupides

Je ne suis pas "un gars". Lorsque je prends des décisions ici, c'est le reflet du processus décisionnel de toute l'équipe TypeScript. L'équipe de conception s'est penchée sur ce problème à plusieurs reprises et est arrivée à chaque fois à la même conclusion. Vous pouvez être en désaccord avec ce résultat, mais le processus ne fait pas l'objet d'un débat.

vous n'avez aucun processus pour faire en sorte que la communauté valide ce dont nous avons besoin ou non.

C'est le processus, ici: Vous appeler mon raisonnement de résumé « muet » et moi (et d' autres personnes de l'équipe) lire. Nous écoutons les commentaires.

Je sais que pointer vers d'autres fonctionnalités implémentées comme raisons de considérer celle-ci est une odeur et pas vraiment un argument, mais je trouve ironique qu'un mécanisme d'alias de type ait été envisagé et ajouté (cela peut bien être génial, mais on pourrait certainement s'en passer ) mais quelque chose comme ça est rejeté.

En fin de compte, je peux vivre sans une telle fonctionnalité, mais la même chose pourrait être dite pour environ la moitié des fonctionnalités de TS. Alors, quelle serait une raison appropriée pour envisager de mettre cela en œuvre ?

GitHub cache inutilement beaucoup de commentaires ici, je republie donc diverses réponses (ainsi que quelques ajouts) que nous avons données dans le passé. Ajout de quelques réflexions ci-dessous également.


Certains points ont été pris en compte lors de notre dernière revue

  • LES CLASSES SONT UNE FONCTIONNALITÉ JAVASCRIPT, PAS UNE FONCTION TYPESCRIPT . Si vous avez vraiment l'impression que les cours sont complètement inutiles sans final , c'est quelque chose à aborder avec le comité TC39 ou à apporter au es-discussion. Si personne ne veut ou ne peut convaincre TC39 que final est une fonctionnalité indispensable, alors nous pouvons envisager de l'ajouter à TypeScript.
  • S'il est légal d'invoquer new sur quelque chose, cela a une correspondance d'exécution un à un avec l'héritage de celui-ci. La notion de quelque chose qui peut être new 'd mais pas super 'd n'existe tout simplement pas en JavaScript
  • Nous faisons de notre mieux pour éviter de transformer TypeScript en une soupe de mots clés POO. Il existe de nombreuses meilleures mécaniques de composition que l'héritage en JavaScript et nous n'avons pas besoin d'avoir tous les mots-clés de C# et Java.
  • Vous pouvez trivialement écrire une vérification d'exécution pour détecter l'héritage d'une classe "devrait être finale", ce que vous devriez vraiment faire de toute façon si vous êtes "inquiet" que quelqu'un hérite accidentellement de votre classe car tous vos consommateurs n'utilisent peut-être pas TypeScript .
  • Vous pouvez écrire une règle de charpie pour appliquer une convention stylistique dont certaines classes ne sont pas héritées
  • Le scénario selon lequel quelqu'un hériterait « accidentellement » de votre classe qui devrait être scellée est en quelque sorte étrange. Je dirais également que l'évaluation individuelle de quelqu'un pour savoir s'il est ou non «possible» d'hériter d'une classe donnée est probablement plutôt suspecte.
  • Il y a essentiellement trois raisons de sceller une classe : la sécurité, les performances et l'intention. Deux d'entre eux n'ont pas de sens dans TypeScript, nous devons donc considérer la complexité par rapport au gain de quelque chose qui ne fait qu'un tiers du travail qu'il fait dans d'autres environnements d'exécution.

Je n'ai pas entendu d'argument convaincant expliquant pourquoi TS n'en a pas besoin

Juste un rappel que ce n'est pas comme ça que ça marche. Toutes les fonctionnalités commencent à -100 . De ce poste

Dans certaines de ces questions, les questions demandent pourquoi nous avons « retiré » ou « laissé de côté » une caractéristique spécifique. Cette formulation implique que nous avons commencé avec un langage existant (C++ et Java sont les choix populaires ici), puis que nous avons commencé à supprimer des fonctionnalités jusqu'à ce que nous arrivions à un point qui nous plaisait. Et, bien que cela puisse être difficile à croire pour certains, ce n'est pas ainsi que le langage a été conçu.

Nous avons un budget de complexité finie. Tout ce que nous faisons ralentit de 0,1 % chaque étape suivante. Lorsque vous demandez une fonctionnalité, vous demandez que toutes les autres fonctionnalités deviennent un peu plus difficiles, un peu plus lentes, un peu moins possibles. Rien n'entre ici parce que Java l'a ou parce que C# l'a ou parce que ce ne serait que quelques lignes de code ou vous pouvez ajouter un commutateur de ligne de commande . Les fonctionnalités doivent payer pour elles-mêmes et pour tout leur fardeau sur l'avenir.


Qu'est-ce qui changerait d'avis ici ? Les personnes se présentant avec des exemples de code qui n'ont pas fonctionné comme il aurait dû parce que le mot-clé final était manquant .

Le scénario auquel le mot-clé tente de répondre est celui où quelqu'un hérite par erreur d'une classe. Comment hériter par erreur d'une classe ? Ce n'est pas une erreur facile à faire. Les classes JavaScript sont intrinsèquement suffisamment fragiles pour que toute sous-classe doive comprendre les exigences comportementales de sa classe de base, ce qui signifie écrire une forme de documentation, et la première ligne de cette documentation peut simplement être "Ne pas sous-classer cette classe". Ou il peut y avoir un contrôle d'exécution. Ou le constructeur de classe peut être caché derrière une méthode de fabrique. Ou tout autre nombre d'options.

En d'autres termes : le seul processus correct pour hériter d'une classe en JavaScript commence toujours par la lecture de la documentation de cette classe . Il y a trop de contraintes que la classe de base pourrait avoir pour simplement dériver et commencer à coder. Si, à la toute première étape , vous devez déjà savoir qu'il ne faut pas essayer, quelle valeur l'application statique fournit-elle à l'étape trois ?

Pourquoi TypeScript doit -

Je programme depuis environ une éternité maintenant et je n'ai pas une seule fois essayé de sous-classer quelque chose pour découvrir qu'il était scellé et que je n'aurais pas dû essayer. Ce n'est pas parce que je suis un super génie ! C'est juste une erreur extrêmement rare à faire, et en JavaScript, ces situations sont déjà celles où vous devez en premier lieu poser des questions à la classe de base. Alors quel problème est en train d'être résolu ?

Si vous n'êtes pas intéressé à renforcer la fonctionnalité principale de Typescript, veuillez le dire à haute voix afin que les nouvelles techniques puissent le relayer sur Twitter

Nous sommes très explicites sur la façon dont nous concevons le langage ; le non-objectif numéro un correspond exactement à cette suggestion.


Ma réaction personnelle après avoir lu les nombreux commentaires ici est qu'il y a un fort effet de biais dû aux constructions qui existent dans d'autres langues. Si vous abordez cela simplement d'un point de vue vierge, vous pouvez imaginer des dizaines de comportements, dont TypeScript n'a qu'un petit sous-ensemble.

Vous pouvez facilement imaginer un mot-clé appliqué à une méthode et imposé que les méthodes dérivées l'appellent via super . Si C# et Java avaient ce mot-clé, les gens appliqueraient absolument ce mot-clé aux endroits où cela a du sens. En fait, il serait sans doute beaucoup plus appliqué que final , car il est impossible d'appliquer un contrôle d'exécution pour, et c'est un aspect beaucoup plus subtil du contrat dérivé de base que "les classes dérivées peuvent ne pas exister ". Ce serait utile à bien des égards, contrairement à final . Je préférerais de loin ce mot-clé que celui-ci (bien que je pense qu'aucun des deux ne répond à la barre de complexité par rapport à la valeur).

Alors, quelle serait une raison appropriée pour envisager de mettre cela en œuvre ?

Lorsque nous examinons les commentaires, les considérations fortes ressemblent à ceci :

  • Je ne peux pas exprimer de manière adéquate le comportement de cette bibliothèque JavaScript
  • Mon code a été compilé, mais n'aurait vraiment pas dû car il y avait cette erreur (subtile)
  • L'intention de ce code n'est pas communiquée de manière adéquate par le système de types

final touche, mais il en va de même pour un modificateur qui dit "Cette fonction ne peut pas être appelée deux fois de suite" ou "La construction de cette classe ne se termine pas vraiment avant le prochain tick asynchrone". Tout ce qui est imaginable ne doit pas exister.

Nous n'avons jamais vu de retours comme "J'ai passé des heures à déboguer parce que j'essayais d'hériter d'une classe qui n'était pas réellement sous-classable" - quelqu'un disant que cela déclencherait à juste titre un "wat" car ce n'est pas vraiment imaginable. Même si le modificateur existait, cela n'aiderait pas vraiment la situation, car les bibliothèques ne documentent pas si les classes sont destinées à être finales - par exemple, fs.FSwatcher final ? Il semble que même les auteurs de nœuds ne le sachent pas. Donc final est suffisant si les auteurs savent qu'il est final , mais qui va être documenté indépendamment, et un manque de final ne vous dit pas vraiment quoi que ce soit parce qu'il est souvent tout simplement pas connu de toute façon.

J'ai lu tout le bloc et j'ai compris la déclaration de l'équipe sur le choix des fonctionnalités, je vais juste revenir sur certains points de mes commentaires

Je ne suis pas "un gars". Lorsque je prends des décisions ici, c'est le reflet du processus décisionnel de toute l'équipe TypeScript.

Désolé, je faisais référence à mhegazy et aux commentaires assez massifs qu'il a reçus de la communauté qui utilise Typescript.

Si vous avez vraiment l'impression que les cours sont complètement inutiles sans final, c'est quelque chose à aborder avec le comité TC39 ou à apporter pour discuter. Si personne ne veut ou ne peut convaincre TC39 que final est une fonctionnalité indispensable, alors nous pouvons envisager de l'ajouter à TypeScript.

Je ne pense pas que final soit la première étape car il y a déjà une proposition pour le mot-clé private https://github.com/tc39/proposal-private-methods

Je suis sceptique sur le You should a comment to say *dont do this* , c'est comme dire sur un formulaire Hey don't write down specific characters , vous avez codé pendant presque toujours des années donc vous devriez connaître la règle n°1 d'un dev : ne jamais faire confiance à l'utilisateur ( et le dev qui utilisera votre code)

Je ne dis pas qu'il faut absolument tout arrêter et mettre un milliard de dollars pour implémenter le mot-clé final, car les usecases sont trop faibles pour être aussi efficaces, considérez également que j'ai donné quelques exemples où la limite vient à la fois de TS et de JS et que si les gens choisissent TS, c'est pour éviter les erreurs au moment de la compilation, pas au moment de l'exécution. Si nous voulons que les choses explosent à l'exécution, nous pouvons utiliser JS et arrêter de nous soucier de TS, mais ce n'est pas le problème, car il existe un cas d'utilisation ultime qui montre comment j'utilise le mot-clé final : je veux verrouiller un méthode, je ne veux pas que quiconque l'ignore.

Et comme Javascript est limité par cela, la communauté a pensé que Typescript pourrait aller au-delà de cette limite, c'est pourquoi ce problème existe depuis 3 ans, c'est pourquoi les gens se demandent toujours pourquoi cette fonctionnalité n'est pas là, et c'est pourquoi nous voulons que le compilateur faire la vérification manuelle d'une classe et/ou d'une méthode.

Vous n'avez pas attendu JS pour avoir des méthodes privées/publiques pour les implémenter, bien qu'il y ait en fait des propositions pour les inclure avec le mot-clé # (moins verbeux que public / private ), je sais que vous vouliez créer un langage parfait parce que la perfection n'est pas quand vous ne pouvez plus rien ajouter, c'est quand vous ne pouvez plus rien enlever.

Si vous pouvez trouver une solution pour empêcher qu'une méthode ne soit écrasée dans le processus de compilation (et non dans le processus d'exécution car le point ne serait pas valide), soyez mon invité.

J'ai essayé de montrer la faiblesse de la situation (sur les méthodes/propriétés et non sur la classe), car tout développeur TS peut réécrire toutes les bibliothèques qu'il veut, en cassant tout ce qu'il veut, c'est peut-être la beauté de la chose, je suppose.

Merci pour la réponse au fait, pas de haine ou de mauvais comportement envers l'équipe de développement ou vous.

Tous les points valables ! Mais, s'il vous plaît, partageons la discussion entre

a) Soutenir les classes finales
b) Soutenir les méthodes finales

Alors que tous vos arguments ciblent a) - aucun ne cible b.

Il est crucial d'avoir des méthodes définitives, comme cela a été souligné à maintes reprises au cours de la discussion. La seule réponse que j'ai entendue jusqu'à présent était "ne pas faire de POO" - mais ce n'est pas ce que j'irais (et beaucoup d'autres) avec.

Suis 28.01.2019 à 20:32 schrieb Ryan Cavanaugh [email protected] :

GitHub cache inutilement beaucoup de commentaires ici, je republie donc diverses réponses (ainsi que quelques ajouts) que nous avons données dans le passé. Ajout de quelques réflexions ci-dessous également.

Certains points ont été pris en compte lors de notre dernière revue

LES CLASSES SONT UNE FONCTIONNALITÉ JAVASCRIPT, PAS UNE FONCTION TYPESCRIPT. Si vous avez vraiment l'impression que les cours sont complètement inutiles sans final, c'est quelque chose à aborder avec le comité TC39 ou à apporter pour discuter. Si personne ne veut ou ne peut convaincre TC39 que final est une fonctionnalité indispensable, alors nous pouvons envisager de l'ajouter à TypeScript.
S'il est légal d'invoquer new sur quelque chose, cela a une correspondance d'exécution un à un avec l'héritage de celui-ci. La notion de quelque chose qui peut être nouveau mais pas super n'existe tout simplement pas en JavaScript
Nous faisons de notre mieux pour éviter de transformer TypeScript en une soupe de mots clés POO. Il existe de nombreuses meilleures mécaniques de composition que l'héritage en JavaScript et nous n'avons pas besoin d'avoir tous les mots-clés de C# et Java.
Vous pouvez trivialement écrire une vérification d'exécution pour détecter l'héritage d'une classe "devrait être finale", ce que vous devriez vraiment faire de toute façon si vous êtes "inquiet" que quelqu'un hérite accidentellement de votre classe car tous vos consommateurs n'utilisent peut-être pas TypeScript .
Vous pouvez écrire une règle de charpie pour appliquer une convention stylistique dont certaines classes ne sont pas héritées
Le scénario selon lequel quelqu'un hériterait « accidentellement » de votre classe qui devrait être scellée est en quelque sorte étrange. Je dirais également que l'évaluation individuelle de quelqu'un pour savoir s'il est ou non «possible» d'hériter d'une classe donnée est probablement plutôt suspecte.
Il y a essentiellement trois raisons de sceller une classe : la sécurité, les performances et l'intention. Deux d'entre eux n'ont pas de sens dans TypeScript, nous devons donc considérer la complexité par rapport au gain de quelque chose qui ne fait qu'un tiers du travail qu'il fait dans d'autres environnements d'exécution.
Je n'ai pas entendu d'argument convaincant expliquant pourquoi TS n'en a pas besoin

Juste un rappel que ce n'est pas comme ça que ça marche. Toutes les fonctionnalités commencent à -100 https://blogs.msdn.microsoft.com/ericgu/2004/01/12/minus-100-points/ . De ce poste

Dans certaines de ces questions, les questions demandent pourquoi nous avons « retiré » ou « laissé de côté » une caractéristique spécifique. Cette formulation implique que nous avons commencé avec un langage existant (C++ et Java sont les choix populaires ici), puis que nous avons commencé à supprimer des fonctionnalités jusqu'à ce que nous arrivions à un point qui nous plaisait. Et, bien que cela puisse être difficile à croire pour certains, ce n'est pas ainsi que le langage a été conçu.

Nous avons un budget de complexité finie. Tout ce que nous faisons ralentit de 0,1 % chaque étape suivante. Lorsque vous demandez une fonctionnalité, vous demandez que toutes les autres fonctionnalités deviennent un peu plus difficiles, un peu plus lentes, un peu moins possibles. Rien n'entre ici parce que Java l'a ou parce que C# l'a ou parce que ce ne serait que quelques lignes de code ou vous pouvez ajouter un commutateur de ligne de commande. Les fonctionnalités doivent payer pour elles-mêmes et pour tout leur fardeau sur l'avenir.

Qu'est-ce qui changerait d'avis ici ? Les gens se présentent avec des exemples de code qui n'ont pas fonctionné comme ils auraient dû parce que le mot-clé final était manquant.

Le scénario auquel le mot-clé tente de répondre est celui où quelqu'un hérite par erreur d'une classe. Comment hériter par erreur d'une classe ? Ce n'est pas une erreur facile à faire. Les classes JavaScript sont intrinsèquement suffisamment fragiles pour que toute sous-classe doive comprendre les exigences comportementales de sa classe de base, ce qui signifie écrire une forme de documentation, et la première ligne de cette documentation peut simplement être "Ne pas sous-classer cette classe". Ou il peut y avoir un contrôle d'exécution. Ou le constructeur de classe peut être caché derrière une méthode de fabrique. Ou tout autre nombre d'options.

En d'autres termes : le seul processus correct pour hériter d'une classe en JavaScript commence toujours par la lecture de la documentation de cette classe. Il y a trop de contraintes que la classe de base pourrait avoir pour simplement dériver et commencer à coder. Si, à la toute première étape, vous devez déjà savoir qu'il ne faut pas essayer, quelle valeur l'application statique fournit-elle à l'étape trois ?

Pourquoi TypeScript doit-il avoir ce mot-clé alors que la situation en est déjà une dans laquelle vous auriez dû rencontrer au moins deux barrages routiers distincts vous disant de ne pas être là pour commencer ?

Je programme depuis environ une éternité maintenant et je n'ai pas une seule fois essayé de sous-classer quelque chose pour découvrir qu'il était scellé et que je n'aurais pas dû essayer. Ce n'est pas parce que je suis un super génie ! C'est juste une erreur extrêmement rare à faire, et en JavaScript, ces situations sont déjà celles où vous devez en premier lieu poser des questions à la classe de base. Alors quel problème est en train d'être résolu ?

Si vous n'êtes pas intéressé à renforcer la fonctionnalité principale de Typescript, veuillez le dire à haute voix afin que les nouvelles techniques puissent le relayer sur Twitter

Nous sommes très explicites sur la façon dont nous concevons le langage https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals ; le non-objectif numéro un correspond exactement à cette suggestion.

Ma réaction personnelle après avoir lu les nombreux commentaires ici est qu'il y a un fort effet de biais dû aux constructions qui existent dans d'autres langues. Si vous abordez cela simplement d'un point de vue vierge, vous pouvez imaginer des dizaines de comportements, dont TypeScript n'a qu'un petit sous-ensemble.

Vous pouvez facilement imaginer un mot-clé appliqué à une méthode et appliqué que les méthodes dérivées l'appelaient via super https://github.com/Microsoft/TypeScript/issues/21388 . Si C# et Java avaient ce mot-clé, les gens appliqueraient absolument ce mot-clé aux endroits où cela a du sens. En fait, il serait sans doute beaucoup plus appliqué que final, car il est impossible d'appliquer un contrôle d'exécution et constitue un aspect beaucoup plus subtil du contrat dérivé de base que "les classes dérivées peuvent ne pas exister". Ce serait utile à bien des égards que la finale ne le serait pas. Je préférerais de loin ce mot-clé que celui-ci (bien que je pense qu'aucun des deux ne répond à la barre de complexité par rapport à la valeur).

Alors, quelle serait une raison appropriée pour envisager de mettre cela en œuvre ?

Lorsque nous examinons les commentaires, les considérations fortes ressemblent à ceci :

Je ne peux pas exprimer de manière adéquate le comportement de cette bibliothèque JavaScript
Mon code a été compilé, mais n'aurait vraiment pas dû car il y avait cette erreur (subtile)
L'intention de ce code n'est pas communiquée de manière adéquate par le système de types
final les frappe, mais il en serait de même pour un modificateur qui dit "Cette fonction ne peut pas être appelée deux fois de suite" ou "La construction de cette classe ne se termine pas vraiment avant le prochain tick asynchrone". Tout ce qui est imaginable ne doit pas exister.

Nous n'avons jamais vu de retours comme "J'ai passé des heures à déboguer parce que j'essayais d'hériter d'une classe qui n'était pas réellement sous-classable" - quelqu'un disant que cela déclencherait à juste titre un "wat" car ce n'est pas vraiment imaginable. Même si le modificateur existait, cela n'aiderait pas vraiment la situation, car les bibliothèques ne documentent pas si les classes sont destinées à être finales - par exemple, est fs.FSwatcher https://nodejs.org/api/fs.html#fs_class_fs_fswatcher final ? Il semble que même les auteurs de nœuds ne le sachent pas. Donc final est suffisant si les auteurs savent que c'est final, mais cela va être documenté de toute façon, et l'absence de final ne vous dit vraiment rien parce que souvent, ce n'est tout simplement pas connu de toute façon.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, affichez-le sur GitHub https://github.com/Microsoft/TypeScript/issues/8306#issuecomment-458268725 , ou coupez le fil https://github.com/notifications/unsubscribe-auth/AA3NA4o9wu54CcJyK1C8WHVjXIQwfmsUksgaIP8M3

Ouais, la discussion est tellement diffuse maintenant. J'aimerais voir ces problèmes dans un tracker où vous pouvez voter, comme celui utilisé pour les problèmes IntelliJ / webstorm. Obtenez des chiffres réels sur le nombre de personnes qui aimeraient voir final pour les classes et/ou les méthodes.

la finale serait très utile

Un mot-clé abstrait est utilisé pour rappeler aux programmeurs qu'ils doivent remplacer le champ. Un mot-clé pour leur rappeler de ne pas l'ignorer sera utile de la même manière.

Les méthodes finales peuvent être cruciales pour rendre le code plus structuré et/ou sécurisé.

Par exemple, disons que vous avez une application qui prend en charge les plugins tiers. Vous pouvez forcer tous les plugins à étendre une classe abstraite qui implémente une interface pour toutes les méthodes qu'ils DOIVENT créer et contient quelques méthodes finales qui contrôlent l'ordre des opérations et vous permettent d'implémenter des crochets entre les opérations. Pour cette raison, votre application n'a pas besoin de connaître les spécificités d'un plug-in individuel, tout en étant consciente de certains de ses états internes.

Sans méthodes finales, ils pourraient interférer avec vos fonctions contrôlant l'ordre des opérations ; pouvant potentiellement provoquer un bogue, un exploit ou tout autre comportement inattendu au sein de votre application. Alors que la plupart des situations auront un certain type de solution de contournement, ces solutions de contournement peuvent être compliquées, répétitives et parfois peu fiables ; alors que les méthodes finales en font une solution simple lorsque la méthode est définie.

De plus, bien que cet exemple soit spécifique aux plugins, il s'applique également à d'autres situations. (nouvelle équipe de développement, s'assurer que certaines logiques ne sont pas hors de portée, standardiser la façon dont le code est exécuté, s'assurer que les méthodes basées sur la sécurité ne sont pas modifiées, etc. tout pourrait en bénéficier)

@RyanCavanaugh Bien qu'il y ait un bon exemple avec render() voulant final, personne n'a mentionné le principe ouvert-fermé (maintenant acronyme à la deuxième place dans le cadre de SOLID).
Pour écrire des frameworks que d'autres utiliseront, cela serait utile.
Si non, quelle est l'approche préférée pour adopter l'approche ouverte-fermée dans Typescript ?

J'adorerais un final pour les méthodes (et je suppose que les propriétés et les déclarations en général). Cela cause de vrais problèmes - les gens n'arrêtent pas de contourner les choses par accident sans se rendre compte qu'il y en avait un en dessous... casser des trucs...

Je pense également que les méthodes finales seraient d'une grande valeur, et comme d'autres l'ont souligné, tous les arguments contre semblent se concentrer sur les classes finales. Il y avait un problème séparé (#9264) mais il a été fermé car apparemment ce problème le couvre, mais je ne pense vraiment pas que ce soit le cas. Pourrions-nous soit rouvrir ce problème, soit le résoudre correctement ici ?

Ma situation est similaire à @ 0815fox où j'ai une version "interne" d'une méthode, qui est abstraite, mais je veux que la version "originale" de la méthode ne soit jamais remplacée, car elle contient un comportement important et je ne veux pas une sous-classe pour remplacer celle-ci.

Je trouve déconcertant que peu importe le soutien écrasant de la communauté en faveur de cette fonctionnalité, elle soit toujours refusée par les développeurs de TypeScript...

peut-être qu'un écart serait d'ajouter une norme à TS-Doc où il serait conseillé que quelqu'un n'hérite pas d'une classe particulière ou ne remplace pas une méthode particulière. Comme une annotation @final ou quelque chose de similaire.

Eh bien, que dois-je ajouter à ce que tous les autres, votant pour "final", ont dit... Voici mon vote pour cela.

D'accord. Bien sûr, vous pouvez utiliser readonly avec des méthodes si vous le traitez comme une propriété de type fonction, mais cette solution de contournement peut être assez déroutante. Ce serait bien d'avoir une solution native à ce problème.

Je pense que final , en particulier final method est important dans la vérification de type, car il contrôle souvent le flux de travail des méthodes, qui garantissent l'ordre des méthodes appelées dans le modèle de modèle ou d'autres modèles de conception.

final devrait être la voie à suivre

Gang - puisque ce ticket est (a) fermé et (b) a un titre trompeur pour les discussions sur la "méthode finale", j'ai décidé de créer un nouveau ticket axé uniquement sur les méthodes finales. Espérons que cela sera plus difficile à ignorer et pourra même stimuler certains progrès. Oh ouais voir https://github.com/microsoft/TypeScript/issues/33446

@RyanCavanaugh

complexité vs barre de valeur

Pourriez-vous préciser ce qui est si complexe à propos de final ? Il me semble que ce serait l'un des mots-clés les plus faciles à implémenter, d'autant plus que des mots-clés tels que abstract sont déjà implémentés, et une grande partie de la logique/code pourrait être réutilisée à partir de cela.

@vinayluzrao

Juste à titre d'exemple aléatoire, nous disons actuellement que vous pouvez hériter de tout ce qui a une signature de construction :

// OK
declare const SomeParent: new() => { a: string };
class A extends SomeParent { }

Évidemment, une classe final est new -able :

function make<T>(ctor: new() => T) {
  return ctor;
}
final class Done {
}
// No problem
const d = make(Done); // d: Done

Mais maintenant, il y a une contradiction : nous avons une chose qui est new -able, mais il n'est pas légal de mettre une clause extends . Donc un niveau d'indirection bat final :

function subvertFinal(ctor: new() => any) {
  class Mwhahaah extends ctor {
    constructor() {
      super();
      console.log("I extended a final class! So much for 'types'!")
    }
  }
}
final class Done {
}
subvertFinal(Done);

Cela fait déjà trébucher les gens sur abstract et les gens soutiennent constamment que les classes abstract devraient avoir une signature de construction implicite qui existe à des fins de vérification de type mais qui n'est pas réellement invocable ou quelque chose du genre.

Pour info, 90 % du temps, lorsque nous parlons de « complexité » dans ces discussions, nous entendons la complexité conceptuelle telle qu'elle est vécue par les utilisateurs de TypeScript , et non la complexité de mise en œuvre de notre côté en tant que développeurs du produit. Ce dernier est quelque chose qui peut être traité (les types conditionnels et les types mappés sont extraordinairement complexes du point de vue de l'implémentation, par exemple) ; la complexité conceptuelle n'est pas quelque chose que vous pouvez gérer en lançant des ingénieurs plus nombreux et plus intelligents sur le produit. Chaque fois que nous ajoutons quelque chose à la langue qui provoque un comportement inattendu ou difficile à expliquer, c'est un fardeau mental pour chaque utilisateur de la langue.

J'ai juste besoin de final pour les méthodes, car sur une base relativement régulière, quelqu'un remplace (sans le savoir) une méthode en dessous, donc mon code ne s'exécute pas, et les choses se cassent de manière très étrange, et les gens ne savent pas pourquoi, et le débogage prend souvent beaucoup de temps.

@RyanCavanaugh qui le clarifie vraiment, merci. Cependant, je voudrais lancer une pensée : il me semble que le problème dans l'exemple que vous avez donné est dû au couplage fort entre new et extends , c'est peut-être le plus gros problème , et non final lui-même. Je me demande pourquoi cette décision de couplage a été prise et à quel point il serait difficile de l'annuler (ce qui n'implique pas du tout qu'il devrait être annulé) à ce stade.

Je ne sais pas si cela pourrait être une discussion trop tangentielle à avoir ici, cependant.

C'est un problème que j'ai rencontré. Un tel exemple de code simple ne peut pas être compilé.
final mot-clé PARFAITEMENT :

abstract class Parent {
    public abstract method(): this;
}

class Child extends Parent {
    public method(): this {
        return new Child();
    }
}

Le type « Enfant » n'est pas attribuable au type « ceci ».
'Child' est assignable à la contrainte de type 'this', mais 'this' pourrait être instancié avec un sous-type différent de contrainte 'Child'.

Terrain de jeux

cela peut-il être rouvert s'il vous plait ? il est évident qu'il y a un besoin pour cette fonctionnalité

Nous avons discuté de cette question une douzaine de fois lors de diverses réunions et à chaque fois, nous nous sommes prononcés contre. Rouvrir ceci pour discuter une treizième fois n'est pas une bonne utilisation de notre temps.

Vous pouvez être en désaccord avec les décisions de l'équipe TypeScript, ce qui est bien, mais nous ne pouvons pas passer toute la journée à tourner en rond pour re-re-re-re-re-re-re-décider des décisions avec lesquelles les gens ne sont pas d'accord. Il y a littéralement un millier d'autres suggestions dans ce référentiel qui méritent un examen initial avant que celui-ci ne soit à nouveau évoqué.

Une discussion constructive comme le commentaire de @nicky1038 est la bienvenue ici ; Je demanderais aux gens de ne pas pinger continuellement ce fil pour les demandes de nouvelles/réexamen afin que nous n'ayons pas à le verrouiller.

Je suis ici pour soutenir l'ajout de final pour les méthodes également. J'ai une classe de base avec une fonction appelée _init_ que je ne veux pas que les sous-classes puissent remplacer. Final serait la solution parfaite.

Je prends également en charge l'ajout du mot-clé final pour les méthodes.
De nos jours, c'est un standard dans la programmation orientée objet.

L'ajout de final sens pour moi. Surtout lors de la création de bibliothèques/frameworks à utiliser par d'autres, où vous ne voulez même pas autoriser le risque de remplacer une fonction.

Si je ne me trompe pas, ajouter final pourrait aider à éviter les tracas avec les erreurs 'myObj' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'MyClass' lorsque je travaille avec des génériques car je pourrais faire MyClass final et donc garantir là-bas ne sont pas d'autres sous-types.
Pas sûr à 100%, mais je pense que c'est ainsi que vous pouvez résoudre ce problème en Java.

Nous avons la même chose, nous créons un composant de base en angulaire que beaucoup d'autres packages peuvent ou doivent étendre en tant que base.
En cela, nous avons des choses comme ngOnChanges ou même ngOnInit d'angular que seule la classe de base devrait implémenter et les sous-classes ne devraient pas les remplacer mais vraiment implémenter le ngOnInitReal (ou comme nous l'appelons) afin que nous contrôlions pleinement quand cela est appelé et avec quoi.
Parce que nous ne pouvons pas contrôler cela, nous ne pouvons le faire que par la documentation. Si un fabricant de composants ne lit pas ceci, son composant peut se casser ou il doit copier beaucoup de code de notre base pour faire la même chose.

Donc, sans méthodes finales, nous ne pouvons pas vraiment faire un contrat d'API solide comme le roc.

quelqu'un a-t-il mentionné

class Fancy {
    readonly secureFoo = (message: string) => {
        console.log(message);
    }
}

quelqu'un a-t-il mentionné

class Fancy {
    readonly secureFoo = (message: string) => {
        console.log(message);
    }
}

Bien que vous puissiez le faire, il existe quelques différences. Par exemple, chaque instance de la classe aura sa propre copie de la fonction, par opposition au seul prototype ayant la fonction que tout le monde utilise.

Cette page vous a été utile?
0 / 5 - 0 notes