public static const in TypeScript
Gibt es so etwas wie öffentliche statische Konstanten in TypeScript? Ich habe eine Klasse, die so aussieht:
export class Library {
public static BOOK_SHELF_NONE: string = "None";
public static BOOK_SHELF_FULL: string = "Full";
}
In dieser Klasse kann ich Library.BOOK_SHELF_NONE machen und tsc beschwert sich nicht. Aber wenn ich versuche, die Klasse Library woanders zu verwenden und dasselbe zu tun, erkennt es sie nicht.
Antwort #1
Wenn Sie etwas wollten, das sich in modernen Browsern eher wie ein statischer Konstantwert verhält (in dem Sinne, dass es von anderem Code nicht geändert werden kann), könnten Sie der Library-Klasse einen nur get-Accessor hinzufügen (dies funktioniert nur für ES5+ Browser und NodeJS):
export class Library {
public static get BOOK_SHELF_NONE():string { return "None"; }
public static get BOOK_SHELF_FULL():string { return "Full"; }
}
var x = Library.BOOK_SHELF_NONE;
console.log(x);
Library.BOOK_SHELF_NONE = "Not Full";
x = Library.BOOK_SHELF_NONE;
console.log(x);
Wenn Sie es ausführen, werden Sie sehen, wie der Versuch, die BOOK_SHELF_NONE-Eigenschaft auf einen neuen Wert zu setzen, nicht funktioniert.
2.0
In TypeScript 2.0 können Sie readonly verwenden, um sehr ähnliche Ergebnisse zu erzielen:
export class Library {
public static readonly BOOK_SHELF_NONE = "None";
public static readonly BOOK_SHELF_FULL = "Full";
}
Die Syntax ist etwas einfacher und offensichtlicher. Der Compiler verhindert jedoch Änderungen, anstatt die Laufzeit (anders als im ersten Beispiel, wo die Änderung überhaupt nicht erlaubt wäre, wie gezeigt).
Antwort #2
Du kannst es mit Namespaces machen, so:
export namespace Library {
export const BOOK_SHELF_NONE: string = 'NONE';
}
Dann können Sie es von irgendwo anders importieren:
import {Library} from './Library';
console.log(Library.BOOK_SHELF_NONE);
Wenn Sie dort ebenfalls eine Klasse benötigen, fügen Sie sie innerhalb des Namensraums ein: export class Book {...}
Antwort #3
Hier ist, wozu dieses TS-Snippet kompiliert wird (über TS Playground):
define(["require", "exports"], function(require, exports) {
var Library = (function () {
function Library() {
}
Library.BOOK_SHELF_NONE = "None";
Library.BOOK_SHELF_FULL = "Full";
return Library;
})();
exports.Library = Library;
});
Wie Sie sehen, sind beide als public static definierten Eigenschaften einfach an die exportierte Funktion angehängt (als deren Eigenschaften); daher sollten sie zugänglich sein, solange Sie korrekt auf die Funktion selbst zugreifen.
Antwort #4
In der Zwischenzeit kann dies durch einen Dekorator in Kombination mit Object.freeze oder Object.defineProperty gelöst werden, ich benutze dies, es ist ein wenig hübscher als die Verwendung von Tonnen von Gettern. Sie können dies direkt in den TS Playground kopieren/einfügen, um es in Aktion zu sehen. - Es gibt zwei Optionen
Machen Sie einzelne Felder „final“
Der folgende Dekorator konvertiert sowohl annotierte statische als auch nicht-statische Felder in „Nur-Lese-Eigenschaften“.
Hinweis: Wenn eine Instanzvariable ohne Anfangswert mit @final annotiert ist, wird der zuerst zugewiesene Wert (egal wann) der endgültige sein.
// example
class MyClass {
@final
public finalProp: string = "You shall not change me!";
@final
public static FINAL_FIELD: number = 75;
public static NON_FINAL: string = "I am not final."
}
var myInstance: MyClass = new MyClass();
myInstance.finalProp = "Was I changed?";
MyClass.FINAL_FIELD = 123;
MyClass.NON_FINAL = "I was changed.";
console.log(myInstance.finalProp); // => You shall not change me!
console.log(MyClass.FINAL_FIELD); // => 75
console.log(MyClass.NON_FINAL); // => I was changed.
Der Dekorateur: Stellen Sie sicher, dass Sie dies in Ihrem Code einfügen!
/**
* Turns static and non-static fields into getter-only, and therefor renders them "final".
* To use simply annotate the static or non-static field with: @final
*/
function final(target: any, propertyKey: string) {
const value: any = target[propertyKey];
// if it currently has no value, then wait for the first setter-call
// usually the case with non-static fields
if (!value) {
Object.defineProperty(target, propertyKey, {
set: function (value: any) {
Object.defineProperty(this, propertyKey, {
get: function () {
return value;
},
enumerable: true,
configurable: false
});
},
enumerable: true,
configurable: true
});
} else { // else, set it immediatly
Object.defineProperty(target, propertyKey, {
get: function () {
return value;
},
enumerable: true
});
}
}
Als Alternative zu dem obigen Dekorator gäbe es auch eine strikte Version davon, die sogar einen Fehler auslösen würde, wenn jemand versucht, einem Feld einen Wert zuzuweisen, während "use strict"; gesetzt ist. (Dies betrifft jedoch nur den statischen Teil)
/**
* Turns static fields into getter-only, and therefor renders them "final".
* Also throws an error in strict mode if the value is tried to be touched.
* To use simply annotate the static field with: @strictFinal
*/
function strictFinal(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
value: target[propertyKey],
writable: false,
enumerable: true
});
}
Machen Sie jedes statische Feld "final"
Möglicher Nachteil: Dies funktioniert nur für ALLE statischen Mitglieder dieser Klasse oder für keine, kann jedoch nicht auf bestimmte statische Mitglieder angewendet werden.
/**
* Freezes the annotated class, making every static 'final'.
* Usage:
* @StaticsFinal
* class MyClass {
* public static SOME_STATIC: string = "SOME_STATIC";
* //...
* }
*/
function StaticsFinal(target: any) {
Object.freeze(target);
}
// Usage here
@StaticsFinal
class FreezeMe {
public static FROZEN_STATIC: string = "I am frozen";
}
class EditMyStuff {
public static NON_FROZEN_STATIC: string = "I am frozen";
}
// Test here
FreezeMe.FROZEN_STATIC = "I am not frozen.";
EditMyStuff.NON_FROZEN_STATIC = "I am not frozen.";
console.log(FreezeMe.FROZEN_STATIC); // => "I am frozen."
console.log(EditMyStuff.NON_FROZEN_STATIC); // => "I am not frozen."
