Typescript: Modificateur d'accès différent pour getter et setter

Créé le 21 avr. 2015  ·  40Commentaires  ·  Source: microsoft/TypeScript

Serait-il possible d'implémenter un modificateur d'accès différent pour le getter et le setter? De cette façon, le setter pourrait être par exemple privé/protégé et le getter public. Dans certains cas, cela est vraiment utile lorsque la valeur doit être en lecture seule.

Exemple:

class MyClass {
    private _myProp: any;
    private set myProp(value: any) {
        this._myProp = value;
    }
    public get myProp(): any {
        return this._myProp;
    }
}
Declined Suggestion Too Complex

Commentaire le plus utile

Quoi qu'il en soit, j'ajoute mon +1 pour cela, au cas où cela serait à nouveau envisagé à l'avenir...

Tous les 40 commentaires

12

Comme # 12 est fermé avec # 6532, il semble maintenant que ce problème soit hors de portée.
Envisagez-vous maintenant d'implémenter différents modificateurs d'accès ?
Parce que readonly n'est pas suffisant pour résoudre ce qui est décrit ici comme une propriété peut être en fait accessible en écriture à l'intérieur d'une classe.

Je crois que je devrais également déplacer mon exemple ici d'un problème connexe:

declare abstract class Emitter {
    new (): Emitter;
    on: (name:string, handler: (arg:any) => void) => void;
    off: (name:string, handler: (arg:any) => void) => void;
    protected emit: (name:string, arg:any) => void;
}

class Person extends Emitter {
    constructor(name:string) {
        super();
        this.name = name;
    }

    private _name:string;
    get name() {
        return this._name;
    }
    set name(value) {
        if (this._name !== value) {
            this._name = value;
            this.emit('change:name', value);
            //this way is better
            this.updatedAt = new Date();
            //than this way
            this.setUpdatedAt(new Date());
        }
    }

    ////
    private _updatedAt:Date;
    get updatedAt() {
        return this._updatedAt;
    }
    private set updatedAt(value) { //Getter and setter do not agree in visibility
                //some logic and a simple readonly (our absence of a setter) is not enough
        if (this._updatedAt !== value) {
            this._updatedAt = value;
            this.emit('change:updatedAt', value);
        }
    }

    //// method implementation - but what's the point in it?
    private setUpdatedAt(value) {
        if (this._updatedAt !== value) {
            this._updatedAt = value;
            this.emit('change:updatedAt', value);
        }
    }
}

const entity = new Person('Mike');
entity.on('change:updatedAt', console.log.bind(console));
entity.name = 'Thomas';
//but manually setting updatedAt should be forbidden
entity.updatedAt = new Date(); //restricted

Ici, la propriété updatedAt peut en fait avoir un setter mais elle ne devrait pas être accessible en dehors de Person . De plus, ce setter contient une logique complexe et une simple lecture seule ou une absence de setter ne suffit pas.

Je suis d'accord que le setter privé n'est qu'un sucre de syntaxe pour les méthodes privées avec une logique supplémentaire (comme l'émission) mais je pense que c'est incohérent lorsqu'une partie de la logique liée à un champ se trouve dans une propriété (getter) et une autre dans une méthode (setter privé) .

De cette façon, les getters/setters sont implémentés dans C# , je pense qu'il serait utile de permettre une visibilité différente au moment de la compilation, bien que ces contrats soient perdus lors de l'exécution.

La recommandation ici est d'utiliser un getter public en lecture seule et un champ de sauvegarde privé/protégé.

Les accesseurs sont symétriques aux propriétés du système de type. tout ce que nous faisons devra être manifesté dans le type et exprimable sur les propriétés. L'ajout de nouveaux modificateurs d'accès pour activer private_set/public_get augmenterait la complexité du langage et la courbe d'apprentissage, et la valeur obtenue ne correspondrait pas à la complexité ajoutée.

Quoi qu'il en soit, j'ajoute mon +1 pour cela, au cas où cela serait à nouveau envisagé à l'avenir...

J'aimerais encore voir ça. C # l'a, et c'est incroyablement utile dans de nombreuses circonstances, en particulier lorsque vous avez des drapeaux qui veulent être lus en externe à la classe, mais uniquement définis en interne avec private/protected.

Je pense que la suggestion selon laquelle il n'est pas utilisé très souvent est une farce dans la mesure où le modèle n'est pas utilisé car il n'est pas disponible.

C'est un bon ajout au langage : la quantité de complexité que cela ajouterait (du point de vue d'un programmeur dactylographié) est faible. Le concept est facile à comprendre et le compilateur devrait être en mesure de fournir de bons messages d'erreur expliquant pourquoi vous ne pouvez pas accéder aux getters ou aux setters en raison de la portée.

Je suis d'accord avec les utilisateurs ci-dessus, c'est standard pour la plupart des autres langues. L'argument contre cela ne tient pas et la mise en œuvre devrait être reconsidérée.

En fait, j'ai été surpris que vous ne puissiez pas le faire dans TS ... +1 au problème.
Il ne devrait pas y avoir de courbe d'apprentissage accrue avec cela

private get x() { ... }
public set x(value) { ... }

Imo si vous pouvez lire l'anglais, il est clair ce que privé (protégé)/public signifie ici. De plus, si vous définissez les accesseurs en premier lieu, vous savez probablement déjà à quoi ils servent.

Ps À propos de l'erreur : "Les accesseurs getter et setter ne sont pas d'accord en termes de visibilité" - eh bien : c'est exactement ce que je veux qu'ils fassent

Voici deux cas d'utilisation où cela serait pratique :

Backbone.js, pour éviter les appels laids .get() et .set() :

class Whatever {
    public get rotation(): number {
        return this.get('rotation');
    }
    private set rotation(rotation: number) {
        this.set('rotation', rotation);
    }
}

Propriétés que les sous-classes peuvent modifier :

class ExtendMe {
    public get someProperty(): string {
        // some stuff
    }
    protected set someProperty(prop: string) {
        // some stuff
    }
}

J'aimerais vraiment voir cela, car cela me dérange depuis que j'ai commencé à utiliser TypeScript.

Je suis d'accord avec les personnes qui demandent cette fonctionnalité. Je viens d'essayer d'avoir une méthode publique get() et j'ai été surpris de voir que mon set et get doivent être d'accord sur la visibilité.

J'ai lu que vous disiez que c'était trop complexe. Se souvenir de la "façon C++" pourquoi elle est différente de :

private _myAttribute: string;
get myAttribute(): string {...}
setMyAttribute(value: string) {...}

Je peux voir beaucoup de cas d'utilisation pour cela, c'est pourquoi je suis ici en ce moment.
Que se passe-t-il si je veux que myAttribute soit accessible au public mais ne permette d'être modifié qu'à l'intérieur de sa classe ?
Et si je veux ajouter une logique personnalisée à chaque fois que l'attribut est modifié ?
Il peut s'agir d'une règle métier, il peut s'agir simplement d'une journalisation pour comprendre pourquoi une valeur spécifique lui a été attribuée, ainsi qu'une condition de point d'arrêt à des fins de débogage, etc.

Fondamentalement, nous voulons qu'une méthode soit utilisée chaque fois que nous modifions l'attribut avec le sucre syntaxique qui donne l'impression que nous attribuons simplement une valeur, au lieu d'appeler une méthode.
C # a le concept de propriétés pour cela et je pensais que TS avait hérité de ce concept, mais ne pas autoriser différentes accessibilités est une grande limitation pour les personnes qui pensent comme moi et nous font retomber dans le "style C++".

"C'est trop difficile" ne devrait jamais être une raison pour ne pas implémenter une fonctionnalité incroyablement utile.

J'ajoute mon +1. Il me semble très étrange que dans une langue qui est si complète à tant d'égards, vous ne puissiez pas faire cela.

Surpris de constater que ce problème est marqué comme clos. Cette fonctionnalité est DÉSIRÉE par la communauté. Veuillez rouvrir.
+1

12 n'aborde pas cette question, donc cette question ne devrait pas être fermée avec un commentaire qui fait référence à cette question.

À mon humble avis, c'est une fonctionnalité intéressante, mais aussi juste que je comprenne, c'est juste la situation "eh bien, je ne veux PAS écrire à chaque fois _property à la place, je veux utiliser la propriété de l'ensemble privé"

S'il y a le choix soit d'ajouter une énorme complexité au code de base de TypeScript lui-même (rappelez-vous, plus de complexité inutile - moins de vitesse de développement, plus de temps pour déboguer, écrire des tests, à la fin - des versions moins fréquentes) mais en réduisant l'écriture 1 symbole OU l'écriture ce 1 symbole et bien le gérer... Je préfère écrire 1 symbole une fois de plus à chaque fois. Au final, je ne suis pas un contributeur au cœur de TS et je n'ai absolument pas envie de me confronter à l'éventuelle complexité de ce sucre.

Ce n'est pas un dealbreaker les gars. De plus, il faut du courage et de la sagesse pour repérer la demande comme trop complexe à mettre en œuvre (le résultat augmentant la complexité globale du code), alors chapeau à vous qui développez TypeScript et merci pour votre travail !

(ps est venu ici pour savoir pourquoi pas - à la fin j'ai changé d'avis et je m'occuperai de ça._property = ...
et sera toujours heureux)

@idchlife L'objectif de la propriété set n'est pas d'épargner aux développeurs un trait de soulignement lors de l'attribution d'une valeur. Vous pouvez également ajouter une logique dans le processus set .

Veuillez rouvrir ce problème. C'est clairement une fonctionnalité très recherchée.

+1 J'aimerais voir cela reconsidéré.

Très étrange, votre raisonnement est "cela rendrait les choses confuses" alors que de nombreuses autres langues le font déjà.

Étant donné que tous les 👍 ne font pas grand-chose, je spammerais simplement avec un commentaire 👍 de plus.

Ce serait du sucre syntaxique qui aide à rendre les choses plus lisibles et rapides à mettre en œuvre. De nombreuses langues ont du sucre syntaxique, y compris Typescript. Il ne semble pas y avoir de véritable bon raisonnement pour ne pas le faire et trop complexe n'est jamais une bonne raison de ne pas faire quelque chose, c'est plutôt une échappatoire paresseuse. La résolution de problèmes trop complexes est l'une des raisons pour lesquelles la plupart des gens sont attirés par le génie logiciel. Alors prenez ce problème complexe et résolvez-le d'une manière agréable et pas si complexe. Cela nécessitera-t-il une refactorisation et des changements risqués ? Peut-être mais c'est ainsi que les choses se passent.

Toujours désirable. D'autres langages n'ont aucun problème à prendre en charge une visibilité différente pour les fonctionnalités get et set. Pour ajouter l'injure à l'insulte, Typescript est fait par Microsoft, tout comme C#, mais ce dernier le supporte entièrement :

public string myPropertyWithLimitedAccess { get; private set; }   

Regarde ça. Magnifiquement lisible, et totalement rien de complexe.

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties

Il y a peu de raisons de ne pas soutenir :

private _myProperty: string;

public get myProperty(): string { return this._myProperty; }
private set myProperty(value: string) { this._myProperty = value; }

Des points bonus dans les décisions de conception étranges, car Typescript a été principalement conçu pour que les développeurs .NET s'habituent davantage à travailler avec le frontal du navigateur.

+1
Typescript bénéficierait certainement de cette fonctionnalité !

TypeScript est génial même après C #, mais cette limitation inutile m'irrite assez souvent. Veuillez le rouvrir.

augmenterait la complexité de la langue

Est-ce vraiment si complexe comparé à des choses comme readonly [P in keyof T]: T[P] ?

Cogner. Tout le monde veut cette fonctionnalité. La communauté TypesScript ne devrait-elle pas décider ?

augmenterait la complexité de la langue

Est-ce vraiment si complexe comparé à des choses comme readonly [P in keyof T]: T[P] ?

La complexité entre en jeu avec l'interaction d'autres caractéristiques du langage. Malheureusement, je ne le trouve pas, mais IIRC RyanCavanaugh a donné un exemple où cette fonctionnalité pourrait vous permettre de configurer puis de violer des invariants via l'héritage à l'aide d'une expression de classe. Ce n'est pas parce qu'il est facile d'écrire une certaine déclaration qu'il sera toujours facile de raisonner sur la façon dont cela affecte les choses.

La question est de savoir quels problèmes une fonctionnalité donnée résoudra et lesquels créera-t-elle. Le premier est facile à répondre, le second peut étonnamment difficile à répondre. Malheureusement, l'équipe TS semble parfois répondre par la "complexité OMG" au lieu d'illustrer le problème. (Pour être juste, plus ils passent de temps à répondre à la question x pour la nième fois, moins ils ont de temps pour se développer.)

Je ne suis pas d'accord à 100% avec la notion de "la communauté ne devrait-elle pas décider", car s'il y a un consensus parmi les experts qui développent réellement le langage, cela devrait vous dire quelque chose. Mais je pense qu'avec quelque chose d'aussi demandé que cela, une explication réfléchie de l'équipe sur ce qu'est le compromis et pourquoi ils sont contre ce n'est pas trop demander.

Et personnellement, je pense que le compromis en vaut la peine à 1000% dans ce cas. Mais si je ne peux pas prendre la peine de le spécifier et de le faire fonctionner, je suppose que je n'ai pas trop le droit de me plaindre.

Serait-ce le bon moment pour revenir sur ce problème ?

Le modificateur readonly est génial, mais il existe de nombreux cas où vous souhaitez pouvoir modifier la valeur à l'intérieur de la classe tout en gardant tout en lecture seule à l'extérieur.

Je choisis souvent de ne pas rendre la propriété readonly parce que je n'aime pas le bruit qu'un champ de sauvegarde privé + les propriétés ajoutent.

Ce serait formidable s'il y avait du sucre syntaxique pour le faire pour vous. Quelque chose comme:

// Option 1: C# style
public name: string { get; private set; }

// Option 2: Swift style
private(set) name: string

// Option 3: Swift struct-style
public readonly name: string

mutating changeName(name: string) {
  this.name = name
}

// Option 4: New keyword
public frozen name1: string
public readonly name2: string

J'aime l'option 2, imo elle s'intégrerait bien dans le langage TypeScript.

Avec l'option 3, vous ne pouvez modifier que les champs en lecture seule dans les fonctions marquées comme mutating

Avec l'option 4, frozen ne peut être défini que dans le constructeur, readonly peut être défini à l'intérieur de cette classe, et non par des classes externes ou des classes héritant de cette classe.

Pour référence, les idées de @yvbeek sur des modificateurs plus flexibles conviennent mieux à la discussion sur https://github.com/microsoft/TypeScript/issues/37487.

Ce problème est spécifiquement destiné aux getters et aux setters, et a un nombre très élevé de votes positifs ! Je pense qu'il serait utile de donner aux getters et aux setters des modificateurs d'accès différents (et nous pouvons mettre à jour l'ensemble existant de modificateurs dans # 37487 si l'équipe TypeScript décide que c'est une chose acceptable pour aller de l'avant)

Je ne suis pas d'accord à 100% avec la notion de "la communauté ne devrait-elle pas décider", car s'il y a un consensus parmi les experts qui développent réellement le langage, cela devrait vous dire quelque chose. Mais je pense qu'avec quelque chose d'aussi demandé que cela, une explication réfléchie de l'équipe sur ce qu'est le compromis et pourquoi ils sont contre ce n'est pas trop demander.

@snarfblam Ne pas obstruer ce fil avec un commentaire non pertinent, mais je pense que vous avez révélé un principe fondamental sur la façon dont le gouvernement devrait fonctionner.

Ne pas avoir cette fonctionnalité est une vraie douleur pour moi et (entre autres) m'a fait passer de TS/NodeJS à quelque chose de plus... sûr de type. Tout va bien, mais lorsque vous travaillez sur un projet complexe avec beaucoup de structures de données (profondément imbriquées à plusieurs reprises) et que vous ne pouvez pas modéliser correctement les données, vous avez l'impression que ce n'est pas un langage "pour gros garçons".

Dans mon cas particulier, je veux que la propriété soit en lecture seule, mais modifiable de l'intérieur... et également sérialisée en JSON. Trop de cerceaux à franchir.

Cette fonctionnalité peut suivre le même chemin que le _chaînage facultatif_. Les gens demanderont cette fonctionnalité pendant des années et puis finalement elle sera ajoutée à la langue, car elle est pratique et d'autres langues offrent la même fonctionnalité.

Sinon, j'espère qu'une implémentation fera partie d'EcmaScript et fera ensuite son chemin vers TypeScript.

Je suis récemment passé au tapuscrit et j'adore la langue. Je suis vraiment déçu que cette fonctionnalité pratique qui existe dans d'autres langues n'ait pas été implémentée ici. Veuillez reconsidérer son ajout dans une future version, quelle que soit la complexité que vous pensez que cela pourrait ajouter au langage.

J'ai réalisé quelque chose de similaire à C # avec des getters de cette façon:

export class I18nService {
  private static ref: I18nService;

  public static get instance(): I18nService {
    if (!I18nService.ref) {
      I18nService.ref = new I18nService();
    }

    return I18nService.ref;
  }
}

Les erreurs de saisie telles que les suivantes sont faciles à comprendre et ne sont pas compliquées :

Property 'foo' is writable only in protected scope within class 'Blah' and its subclasses.

ou

Property 'foo' is readable only in protected scope within class 'Blah' and its subclasses.

etc, et similaire avec private .

Ce n'est franchement pas compliqué.

BTW, je suis aussi tombé sur ce problème, et j'utilise ce genre de "hack" en attendant:

// somewhere.ts
declare global {
    type Writable<T> = { -readonly [P in keyof T]: T[P]; }
}

// example.ts
class Example {

    public readonly prop: number;

    public doSomething(n: number): void {
        (this as Writable<this>).prop = n;
    }
}

Techniquement, cela pourrait être utilisé partout, mais l'utilisation de cette solution de contournement devrait être limitée au code à l'intérieur des méthodes de classe.

BTW, je suis aussi tombé sur ce problème, et j'utilise ce genre de "hack" en attendant:

J'ai moi-même joué avec cette idée, mais le problème est que vous n'avez aucun moyen de faire la distinction entre les propriétés "publiques en lecture seule" et véritablement en lecture seule. Cela le rend peu approprié comme solution à usage général.

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

Questions connexes

bgrieder picture bgrieder  ·  3Commentaires

dlaberge picture dlaberge  ·  3Commentaires

Antony-Jones picture Antony-Jones  ·  3Commentaires

DanielRosenwasser picture DanielRosenwasser  ·  3Commentaires

manekinekko picture manekinekko  ·  3Commentaires