Typescript: Unterschiedlicher Zugriffsmodifikator für Getter und Setter

Erstellt am 21. Apr. 2015  ·  40Kommentare  ·  Quelle: microsoft/TypeScript

Könnte es möglich sein, unterschiedliche Zugriffsmodifikatoren für Getter und Setter zu implementieren? So könnte beispielsweise der Setter privat/geschützt und der Getter öffentlich sein. In einigen Fällen ist dies wirklich nützlich, wenn der Wert schreibgeschützt sein soll.

Beispiel:

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

Hilfreichster Kommentar

Wie auch immer, füge mein +1 dafür hinzu, falls es in Zukunft wieder in Betracht gezogen wird ...

Alle 40 Kommentare

12

Da #12 jetzt mit #6532 geschlossen wird, scheint dieses Problem außerhalb des Geltungsbereichs zu liegen.
Haben Sie jetzt Pläne, verschiedene Zugriffsmodifikatoren zu implementieren?
Weil readonly nicht ausreicht, um das zu lösen, was hier beschrieben wird, da eine Eigenschaft tatsächlich innerhalb einer Klasse beschreibbar sein könnte.

Ich glaube, ich sollte mein Beispiel auch hier aus dem verwandten Problem verschieben:

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

Hier kann die Eigenschaft updatedAt tatsächlich einen Setter haben, aber sie sollte außerhalb von Person nicht zugänglich sein . Außerdem enthält dieser Setter komplexe Logik und ein einfaches Readonly oder das Fehlen eines Setters ist nicht genug.

Ich stimme zu, dass privater Setter nur ein Syntaxzucker für private Methoden mit zusätzlicher Logik (wie Emittieren) ist, aber ich denke, es ist inkonsistent, wenn ein Teil der Logik in Bezug auf ein Feld in einer Eigenschaft (Getter) und ein anderer in einer Methode (privater Setter) ist. .

Auf diese Weise werden Getter/Setter in C# implementiert. Ich denke, es wäre nützlich, unterschiedliche Sichtbarkeiten zur Kompilierzeit zuzulassen, obwohl diese Verträge zur Laufzeit verloren gehen.

Die Empfehlung hier lautet, einen öffentlichen schreibgeschützten Getter und ein privates/geschütztes Sicherungsfeld zu verwenden.

Accessoren sind symmetrisch zu Eigenschaften im Typsystem. Alles, was wir tun, muss sich in der Art manifestieren und auf Eigenschaften ausdrücken. Das Hinzufügen neuer Zugriffsmodifikatoren zum Aktivieren von private_set/public_get würde die Komplexität der Sprache und die Lernkurve erhöhen, und der daraus gewonnene Wert würde der zusätzlichen Komplexität nicht entsprechen.

Wie auch immer, füge mein +1 dafür hinzu, falls es in Zukunft wieder in Betracht gezogen wird ...

Das möchte ich mir noch ansehen. C# hat es, und es ist in vielen Situationen unglaublich nützlich, besonders wenn Sie Flags haben, die extern von der Klasse gelesen, aber nur intern mit private/protected gesetzt werden sollen.

Ich denke, der Vorschlag, dass es nicht allzu oft verwendet wird, ist eine Farce, da das Muster nicht verwendet wird, weil es nicht verwendet werden kann.

Dies ist eine gute Erweiterung der Sprache: Die Menge an Komplexität, die dies hinzufügen würde (aus der Sicht eines Typoskript-Programmierers), ist gering. Das Konzept ist einfach zu verstehen, und der Compiler sollte in der Lage sein, gute Fehlermeldungen bereitzustellen, die darauf hinweisen, warum Sie aufgrund des Bereichs nicht auf Getter oder Setter zugreifen können.

Ich stimme den oben genannten Benutzern zu, dies ist Standard für die meisten anderen Sprachen. Das Argument dagegen ist nicht stichhaltig und die Umsetzung sollte überdacht werden.

Ich war tatsächlich überrascht, dass Sie dies nicht in TS tun können ... +1 zum Thema.
Es sollte keine erhöhte Lernkurve damit geben

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

Imo, wenn Sie Englisch lesen können, ist es selbsterklärend, was hier privat (geschützt) / öffentlich bedeutet. Auch wenn Sie die Zugriffsmethoden überhaupt erst definieren, wissen Sie wahrscheinlich bereits, wofür sie sind.

Ps. Zu dem Fehler: "Getter- und Setter-Accessoren stimmen in der Sichtbarkeit nicht überein" - nun ja: genau das möchte ich von ihnen

Hier sind zwei Anwendungsfälle, in denen dies praktisch wäre:

Backbone.js, um hässliche .get() - und .set() -Aufrufe zu vermeiden:

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

Eigenschaften, die Unterklassen ändern können:

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

Ich würde das auf jeden Fall gerne sehen, da es mich nervt, seit ich angefangen habe, TypeScript zu verwenden.

Ich stimme den Leuten zu, die diese Funktion anfordern. Ich habe gerade versucht, eine öffentliche get () -Methode zu verwenden, und war überrascht zu sehen, dass mein Set und Get sich auf die Sichtbarkeit einigen müssen.

Ich habe gelesen, dass ihr behauptet, es sei zu komplex. Erinnern Sie sich an den "C++-Weg", warum es anders ist als:

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

Ich sehe viele Anwendungsfälle dafür, deshalb bin ich jetzt hier.
Was ist, wenn ich möchte, dass myAttribute öffentlich zugänglich ist, aber nur innerhalb seiner Klasse geändert werden kann?
Was ist, wenn ich jedes Mal, wenn das Attribut geändert wird, eine benutzerdefinierte Logik hinzufügen möchte?
Es kann eine Geschäftsregel sein, es kann nur eine Protokollierung sein, um zu verstehen, warum ihr ein bestimmter Wert zugewiesen wurde, zusammen mit einer Haltepunktbedingung für Debugging-Zwecke usw.

Grundsätzlich möchten wir, dass jedes Mal, wenn wir das Attribut ändern, eine Methode zusammen mit dem syntaktischen Zucker verwendet wird, der den Anschein erweckt, als würden wir nur einen Wert zuweisen, anstatt eine Methode aufzurufen.
C# hat das Konzept der Eigenschaften dafür und ich dachte, TS hat dieses Konzept geerbt, aber das Nicht-Zulassen unterschiedlicher Zugriffsmöglichkeiten ist eine große Einschränkung für Leute, die wie ich denken und uns auf den "C++-Stil" zurückgreifen lassen.

„Es ist zu schwer“ sollte niemals ein Grund sein, ein unglaublich nützliches Feature nicht zu implementieren.

Füge mein +1 hinzu. Es scheint mir sehr seltsam, dass man das in einer Sprache, die in so vielen Dingen so vollständig ist, nicht tun kann.

Ich bin überrascht, dass dieses Problem als geschlossen markiert ist. Diese Funktion wird von der Community GEWÜNSCHT. Bitte wieder öffnen.
+1

12 behandelt dieses Problem nicht, daher sollte dieses Problem nicht mit einem Kommentar geschlossen werden, der sich auf dieses Problem bezieht.

IMHO ist dies ein nettes Feature, aber so fair ich es verstehe, ist es nur die Situation "Nun, ich möchte nicht jedes Mal _property schreiben, sondern ich möchte privates Set-Eigentum verwenden."

Wenn es die Wahl gibt, entweder dem TypeScript-Kerncode selbst eine enorme Komplexität hinzuzufügen (denken Sie daran, mehr unnötige Komplexität - weniger Geschwindigkeit beim Entwickeln, mehr Zeit zum Debuggen, Schreiben von Tests, am Ende - weniger häufige Releases), aber das Schreiben von 1 Symbol ODER das Schreiben zu reduzieren dass 1 Symbol und gut damit umgehen ... Ich würde lieber jedes Mal 1 Symbol mehr schreiben. Letztendlich bin ich kein Mitwirkender am TS-Kern und ich möchte mich definitiv nicht mit der möglichen Komplexität dieses Zuckers befassen.

Es ist kein Dealbreaker, Jungs. Es erfordert auch Mut und Weisheit, Anfragen als zu komplex für die Implementierung zu erkennen (wobei das Ergebnis die allgemeine Codekomplexität erhöht), also Hut ab vor Ihnen, die TypeScript entwickeln, und danke für Ihre Arbeit!

(ps kam hierher, um zu erfahren, warum nicht - am Ende habe ich meine Meinung geändert und werde mich damit befassen._property = ...
und werde immer noch glücklich sein)

@idchlife Das Ziel der Eigenschaft set besteht nicht darin, Entwicklern bei der Wertzuweisung einen Unterstrich zu ersparen. Vielleicht möchten Sie auch etwas Logik in den set -Prozess einfügen.

Bitte öffnen Sie dieses Problem erneut. Dies ist eindeutig ein höchst erwünschtes Merkmal.

+1 Ich würde gerne sehen, dass dies noch einmal überdacht wird.

Sehr seltsam ist Ihre Begründung "das würde die Dinge verwirrend machen", wenn viele andere Sprachen dies bereits tun.

Da all die 👍s nicht viel machen, würde ich einfach mit einem weiteren 👍-Kommentar spammen.

Dies wäre syntaktischer Zucker, der hilft, die Dinge lesbarer und schneller zu implementieren. Viele Sprachen haben syntaktischen Zucker, einschließlich Typescript. Es scheint keinen wirklich guten Grund zu geben, es nicht zu tun, und zu komplex ist nie ein guter Grund, etwas nicht zu tun, es ist eher ein fauler Ausweg. Das Lösen von zu komplexen Problemen ist einer der Gründe, warum sich die meisten Menschen für Software Engineering interessieren. Nehmen Sie also dieses komplexe Problem und lösen Sie es auf eine nette, nicht so komplexe Weise. Wird es Refactoring und riskante Änderungen erfordern? Vielleicht, aber so laufen die Dinge.

Immer noch wünschenswert. Andere Sprachen haben keine Probleme, unterschiedliche Sichtbarkeiten für Get- und Set-Funktionalität zu unterstützen. Um der Beleidigung noch mehr Schaden zuzufügen, wird Typescript von Microsoft erstellt, genau wie C#, aber letzteres unterstützt es vollständig:

public string myPropertyWithLimitedAccess { get; private set; }   

Sieh dir das an. Schön lesbar und absolut nichts zu Komplexes.

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

Es gibt wenig Grund, nicht zu unterstützen:

private _myProperty: string;

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

Bonuspunkte bei seltsamen Designentscheidungen, da Typescript in erster Linie für .NET-Entwickler gemacht wurde, um sich besser an die Arbeit mit dem Browser-Frontend zu gewöhnen.

+1
Typoskript würde definitiv von dieser Funktionalität profitieren!

TypeScript ist auch nach C# großartig, aber diese sinnlose Einschränkung irritiert mich ziemlich oft. Bitte öffnen Sie es erneut.

würde die Komplexität der Sprache erhöhen

Ist das wirklich so komplex im Vergleich zu Dingen wie readonly [P in keyof T]: T[P] ?

Stoßen. Jeder will diese Funktion. Sollte die TypesScript-Community nicht entscheiden?

würde die Komplexität der Sprache erhöhen

Ist das wirklich so komplex im Vergleich zu Dingen wie readonly [P in keyof T]: T[P] ?

Die Komplexität kommt mit dem Zusammenspiel anderer Sprachmerkmale ins Spiel. Leider kann ich es nicht finden, aber IIRC RyanCavanaugh gab ein Beispiel, in dem Sie mit dieser Funktion Invarianten über Vererbung mithilfe eines Klassenausdrucks einrichten und dann verletzen könnten. Nur weil es einfach ist, eine bestimmte Erklärung zu schreiben, bedeutet das nicht, dass es immer einfach ist, darüber nachzudenken, wie sie sich auf die Dinge auswirkt.

Die Frage ist, welche Probleme ein bestimmtes Feature löst und welche schafft es. Ersteres ist einfach zu beantworten, letzteres kann überraschend schwer zu beantworten sein. Leider scheint das TS-Team manchmal mit "OMG-Komplexität" zu antworten, anstatt das Problem zu veranschaulichen. (Um fair zu sein, je mehr Zeit sie damit verbringen, Frage x zum n-ten Mal zu beantworten, desto weniger Zeit haben sie für die Entwicklung.)

Ich stimme der Aussage "sollte die Community nicht entscheiden dürfen" nicht zu 100 % zu, denn wenn es einen Konsens unter den Experten gibt, die die Sprache tatsächlich entwickeln, sollte Ihnen das etwas sagen. Aber ich denke, bei einer solchen Anforderung ist eine durchdachte Erklärung des Teams, was der Kompromiss ist und warum sie dagegen sind, nicht zu viel verlangt.

Und ich persönlich denke, dass sich der Kompromiss in diesem Fall zu 1000% lohnt. Aber wenn ich mir nicht die Mühe machen kann, es zu spezifizieren und zum Laufen zu bringen, habe ich wohl nicht das Recht, mich zu beschweren.

Wäre dies ein guter Zeitpunkt, um dieses Thema noch einmal aufzugreifen?

Der Modifikator readonly ist großartig, aber es gibt viele Fälle, in denen Sie in der Lage sein möchten, den Wert innerhalb der Klasse zu ändern, und dennoch alles äußerlich schreibgeschützt haben möchten.

Ich entscheide mich oft dafür, die Eigenschaft nicht zu readonly zu machen, weil ich das Rauschen nicht mag, das ein privates Hintergrundfeld + die Eigenschaften hinzufügen.

Es wäre großartig, wenn es etwas syntaktischen Zucker gäbe, der dies für Sie erledigt. Etwas wie:

// 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

Ich mag Option 2, imo würde es gut in die TypeScript-Sprache passen.

Mit Option 3 können Sie nur schreibgeschützte Felder in Funktionen ändern, die als mutating gekennzeichnet sind

Mit Option 4 kann frozen nur im Konstruktor gesetzt werden, readonly kann innerhalb dieser Klasse gesetzt werden, nicht durch externe Klassen oder Klassen, die von dieser Klasse erben.

Als Referenz eignen sich die Ideen von @yvbeek zu flexibleren Modifikatoren besser für die Diskussion unter https://github.com/microsoft/TypeScript/issues/37487.

Diese Ausgabe ist speziell für Getter und Setter und hat eine super hohe Anzahl an Upvotes! Ich denke, es wäre nützlich, Gettern und Settern unterschiedliche Zugriffsmodifikatoren zu geben (und wir können den vorhandenen Satz von Modifikatoren in #37487 aktualisieren, falls das TypeScript-Team jemals entscheidet, dass es eine akzeptable Sache ist, damit fortzufahren).

Ich stimme der Aussage "sollte die Community nicht entscheiden dürfen" nicht zu 100 % zu, denn wenn es einen Konsens unter den Experten gibt, die die Sprache tatsächlich entwickeln, sollte Ihnen das etwas sagen. Aber ich denke, bei einer solchen Anforderung ist eine durchdachte Erklärung des Teams, was der Kompromiss ist und warum sie dagegen sind, nicht zu viel verlangt.

@snarfblam Um diesen Thread nicht mit einem irrelevanten Kommentar zu verstopfen, aber ich denke, Sie haben ein Grundprinzip für die Art und Weise offenbart, wie die Regierung arbeiten sollte.

Diese Funktion nicht zu haben, ist ein echter Schmerz für mich und hat mich (unter anderem) dazu gebracht, von TS/NodeJS zu etwas mehr zu wechseln ... typsicher. Es ist alles schön und gut, aber wenn Sie an einem komplexen Projekt mit vielen Datenstrukturen (oftmals tief verschachtelt) arbeiten und die Daten nicht richtig modellieren können, haben Sie das Gefühl, dass dies keine Sprache "für groß ist Jungen".

In meinem speziellen Fall möchte ich, dass die Eigenschaft schreibgeschützt, aber von innen änderbar ist ... und auch in JSON serialisiert wird. Zu viele Reifen, um durchzuspringen.

Diese Funktion kann dem gleichen Pfad folgen wie _optionale Verkettung_. Die Leute werden jahrelang nach dieser Funktion fragen, und dann wird sie schließlich der Sprache hinzugefügt, weil sie praktisch ist und andere Sprachen die gleiche Funktion bieten.

Ansonsten hoffe ich, dass einige Implementierungen Teil von EcmaScript werden und dann ihren Weg zu TypeScript finden.

Ich bin vor kurzem auf Typoskript umgestiegen und ich liebe die Sprache. Ich bin wirklich sauer, dass dieses praktische Feature, das es in anderen Sprachen gibt, hier nicht implementiert wurde. Bitte denken Sie darüber nach, es in einer zukünftigen Version hinzuzufügen, unabhängig davon, wie viel Komplexität Sie der Sprache hinzufügen könnten.

Auf diese Weise habe ich mit Gettern etwas Ähnliches wie c# erreicht:

export class I18nService {
  private static ref: I18nService;

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

    return I18nService.ref;
  }
}

Tippfehler wie die folgenden sind einfach zu verstehen und nicht kompliziert:

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

oder

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

etc, und ähnlich mit private .

Das ist ehrlich gesagt nicht kompliziert.

Übrigens bin ich auch über dieses Problem gestolpert und verwende inzwischen diese Art von "Hack":

// 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;
    }
}

Technisch gesehen könnte dies überall verwendet werden, aber die Verwendung dieser Problemumgehung sollte auf Code innerhalb von Klassenmethoden beschränkt sein.

Übrigens bin ich auch über dieses Problem gestolpert und verwende inzwischen diese Art von "Hack":

Ich habe selbst mit dieser Idee gespielt, aber das Problem ist, dass Sie keine Möglichkeit haben, zwischen "öffentlichen schreibgeschützten" und wirklich schreibgeschützten Eigenschaften zu unterscheiden. Als Allzwecklösung ist es daher nicht sehr geeignet.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen