2017-02-13 2 views
1

Ich habe folgende Typoskript class:typescript: Warum kann ich `readonly` Eigenschaft im Konstruktor ändern?

class Jedi { 
    private readonly name: string = 'Skywalker'; 
    constructor(name: string) { 
     this.name = name; //Why I am able to modify name. 
    } 
    toString():string { 
     return this.name; 
    } 
} 

var jedi = new Jedi('Kenobi'); 

console.log(jedi.toString()); //Kenobi 

Wie Sie im Code sehen können, habe ich name Eigenschaft als readonly erklärt. Nach meinem Wissen um Eigenschaften als Konstante zu deklarieren verwenden wir readonly in Typoskript. Sobald wir es mit der Initialisierung deklariert haben, können wir es nicht ändern. Aber wie Sie in der constructor sehen können, wird es geändert. Auch typescript Compiler warnt nicht davor. Nicht sicher, ob es sich um einen Fehler in typescript oder absichtlich handelt. Kann mir bitte jemand erklären?

+0

Sobald Sie den Wert festgelegt haben, können Sie keinen anderen Wert neu zuweisen. –

+0

Aber ich kann es zuweisen. Ich bin mir nicht sicher, warum ich es so machen darf. –

+0

Sorry, ich habe diesen Teil verpasst .. Ich dachte, du schreibst es nicht. –

Antwort

2

docs sagen nur

Read-only Eigenschaften bei ihrer Deklaration oder im Konstruktor initialisiert werden muss. (Source)

Aber wie Romain wies darauf hin, gibt es ein wenig mehr Info in den Typoskript 2.0 Release Notes:

Read-only Eigenschaften initializers haben und innerhalb derselben in Konstrukteuren zugewiesen werden können Klassendeklaration, aber ansonsten sind Zuweisungen zu schreibgeschützten Eigenschaften nicht zulässig. (Source)


Aber was mich verwirrt, ist die kompilierte Ausgabe. Siehe ein Beispiel auf der TS Playground.

Wie Sie sehen können, wird die anfängliche (readonly) Eigenschaft ohne Warnung von TS zur Kompilierzeit überschrieben. Ich denke, das ist beabsichtigt, weil sich die Sprache die ganze Zeit gleich verhält und es weniger Randfälle gibt.

EDIT: Es gibt auch eine „sugarized“ Art und Weise eine class mit Eigenschaften in Typoskript zu initialisieren (Ich denke, Sie wissen, dass aber trotzdem):

Hinzufügen public/private auf Argumente des Konstruktors Signatur wird initialisiert diese Argumente als Klasseneigenschaften. Also in Ihrem Beispiel:

class Jedi { 
    constructor(
     private readonly name: string = 'Skywalker' 
    ) {} 

    toString():string { 
     return this.name; 
    } 
} 

Und wenn sie Typoskript den obigen Code kompilieren, es funktioniert tatsächlich wie erwartet (oder zumindest das, was ich erwarten würde). Es gibt noch eine Link to the Playground.

tl; dr; Das Initialisieren von Klasseneigenschaften in der Konstruktorsignatur über public/private setzt den Standardwert, wenn keiner angegeben wird.Dies funktioniert nicht, wenn Sie Klasseneigenschaften "von Hand" setzen (this.name = name). Letzteres ist möglicherweise noch kein Fehler, sondern ein absichtliches Verhalten, da Sie die Klasseneigenschaft explizit festgelegt haben.

Sie können dieses Problem vermeiden (oder mindestens TypScript schreien bei Ihnen), indem Sie noImplicitAny und strictNullChecks aktivieren. Auf diese Weise wird TypeScript Ihnen mitteilen, dass Ihre Klasseneigenschaft möglicherweise undefined ist.


Was würde ich vorschlagen, nicht readonly Eigenschaft verwendet, wenn Sie Unveränderlichkeit haben wollen. Nicht nur ist das oben erwähnte ein bisschen seltsames Verhalten, einige weniger erfahrene Entwickler könnten auch denken, dass ein Array/Objekt wirklich vollständig eingefroren/nur dort gelesen wird, wo es nicht ist. Verwenden Sie lieber etwas wie ImmutableJS.

0

Von der Typoskript Dokumentation:

Read-only Eigenschaften können initializers haben und innerhalb der gleichen Klassendeklaration in Konstrukteuren zugewiesen werden können, aber ansonsten Zuweisungen Eigenschaften zu schreibgeschützt sind nicht zulässig.

More about the readonly keyword

0

Elementvariablen, die als readonly deklariert sind, können mit ihrer Deklaration oder vom Konstruktor zugewiesen werden. Es gibt eine gewisse C# Vertrautheit mit diesem.

Beispiel 1

direkt Hier wird die Nur-Lese-Membervariable zugeordnet, die, wenn Sie nützlich ist, was ein Wert ist, und dass es nie ändern wird.

class Foo { 
    public readonly bar: number = 123; 
} 

Beispiel 2

Hier wird die Nur-Lese-Membervariable deklariert, und dann aus dem Konstruktor zugewiesen, die, wenn Sie den Endwert noch nicht wissen, ist nützlich, aber einige Konstruktor Logik lösche das für dich.

class Foo { 
    public readonly bar: number; 

    constructor(bar: number) { 
     this.bar = 123 * bar/100; 
    } 
} 

Beispiel 3

Hier ist der Nur-Lese-Membervariable verwendet Kurz Hand Membervariable Erklärung Typoskript, wo globale Elementvariablen aus dem Konstruktor zugeordnet werden, was nützlich ist, wenn der Benutzer eingeben möchten ein Wert, der sich dann nie ändert. In diesem speziellen Beispiel kann bar entweder vom Benutzer festgelegt werden oder standardmäßig 123.

class Foo { 
    constructor(public readonly: bar: number = 123) { 
    } 
} 

Beispiel 4

Dies ist etwas, was Sie nicht tun wollen, weil das emittierte JavaScript zwei Aufgaben auf die gleiche Variable verursacht. Ich füge das nur hinzu, damit zukünftige Entwickler wissen, dass sie das wahrscheinlich nicht tun sollten.

class Foo { 
    public readonly bar: number = 123; 

    constructor(bar: number) { 
     this.bar = bar; 
    } 
} 

Das emittierte JavaScript wird in etwa so aussehen ...

var Foo = (function() { 
    function Foo(bar) { 
     this.bar = 123; // :) 
     this.bar = bar; // :(
    } 

    return Foo; 
})(); 

Was Unveränderlichkeit?

readonly wird nur von TypeScript und seinem Compiler unterstützt; Es gibt keine readonly in JavaScript (naja, das ist nicht ganz richtig, nur dass TypeScript nicht wirklich readonly Werte ausgibt). Es gibt ein paar Abhilfen für dieses (und ja, es wäre schön, wenn Typoskript diese für uns emittieren könnte!)

Erstellen eines unveränderlichen Membervariable

class Foo { 
    // TypeScript respects "readonly" 
    public readonly bar: number; 

    constructor(bar: number) { 
     // JavaScript respects property that isn't writable. 
     Object.defineProperty(this, "bar", { 
      value: bar, 
      enumerable: true, 
      configurable: false, 
      writable: false 
     }); 
    } 
} 

eine unveränderliche Klasse erstellen

class Foo { 
    constructor(public readonly bar: number) { 
     // Nothing in the object is allowed to be reassigned. 
     Object.freeze(this); 
    } 
} 
Verwandte Themen