Existe-t-il des constantes statiques publiques dans TypeScript? J'ai un cours qui ressemble à:
export class Library {
public static BOOK_SHELF_NONE: string = "None";
public static BOOK_SHELF_FULL: string = "Full";
}
Dans cette classe, je peux faire Library.BOOK_SHELF_NONE
et le tsc ne se plaint pas. Mais si j'essaie d'utiliser la classe Library ailleurs et d'essayer de faire la même chose, elle ne la reconnaît pas.
Voici en quoi cet extrait TS est compilé (via 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;
});
Comme vous le voyez, les deux propriétés définies en tant que public static
sont simplement attachées à la fonction exportée (en tant que propriétés); ils doivent donc être accessibles tant que vous accédez correctement à la fonction elle-même.
Si vous voulez quelque chose qui se comporte davantage comme une valeur constante statique dans les navigateurs modernes (en ce sens qu'il ne peut pas être modifié par un autre code), vous pouvez ajouter un accesseur get
only à la classe Library
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);
Si vous l'exécutez, vous verrez comment la tentative de définition de la propriété BOOK_SHELF_NONE
sur une nouvelle valeur ne fonctionne pas.
Dans TypeScript 2.0, vous pouvez utiliser readonly
pour obtenir des résultats très similaires:
export class Library {
public static readonly BOOK_SHELF_NONE = "None";
public static readonly BOOK_SHELF_FULL = "Full";
}
La syntaxe est un peu plus simple et plus évidente. Cependant, le compilateur empêche les modifications plutôt que le temps d'exécution (contrairement au premier exemple, où les modifications ne seraient pas autorisées du tout comme indiqué).
Vous pouvez le faire en utilisant namespaces , comme ceci:
export namespace Library {
export const BOOK_SHELF_NONE: string = 'NONE';
}
Ensuite, vous pouvez l'importer de n'importe où:
import {Library} from './Library';
console.log(Library.BOOK_SHELF_NONE);
Si vous avez besoin d'une classe, incluez-la également dans l'espace de noms: export class Book {...}
En attendant, ceci peut être résolu par un décorateur en combinaison avec Object.freeze
ou Object.defineProperty
, je l'utilise, c'est un peu plus joli que d'utiliser des tonnes de getters. Vous pouvez copier/coller ceci directement TS Playground pour le voir en action. - Il y a deux options
Le décorateur suivant convertit les champs statiques et non statiques annotés en "propriétés de lecture seule".
Note: Si une variable d'instance sans valeur initiale est annotée @final
, la première valeur affectée (quelle que soit l'heure) sera la dernière.
// 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.
Le décorateur: Assurez-vous de l'inclure dans votre code!
/**
* 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
});
}
}
En guise d'alternative au décorateur ci-dessus, il existe également une version stricte de cette méthode, qui peut même générer une erreur lorsque quelqu'un tente d'attribuer une valeur au champ avec la valeur "use strict";
définie. (Ceci est seulement la partie statique si)
/**
* 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
});
}
Inconvénient possible: Ceci ne fonctionnera que pour TOUTES les statiques de cette classe ou pour aucun, mais ne peut pas être appliqué à des statiques spécifiques.
/**
* 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."
Merci WiredPrairie!
Juste pour développer un peu votre réponse, voici un exemple complet de définition d'une classe de constantes.
// CYConstants.ts
class CYConstants {
public static get NOT_FOUND(): number { return -1; }
public static get EMPTY_STRING(): string { return ""; }
}
export = CYConstants;
Utiliser
// main.ts
import CYConstants = require("./CYConstants");
console.log(CYConstants.NOT_FOUND); // Prints -1
console.log(CYConstants.EMPTY_STRING); // Prints "" (Nothing!)
La solution suivante fonctionne également à partir de TS 1.7.5.
// Constancts.ts
export const kNotFoundInArray = -1;
export const AppConnectionError = new Error("The application was unable to connect!");
export const ReallySafeExtensions = ["exe", "virus", "1337h4x"];
Utiliser:
// Main.ts
import {ReallySafeExtensions, kNotFoundInArray} from "./Constants";
if (ReallySafeExtensions.indexOf("png") === kNotFoundInArray) {
console.log("PNG's are really unsafe!!!");
}
Vous pouvez utiliser un getter pour que votre propriété lise seulement .. .. Exemple
export class MyClass {
private _LEVELS = {
level1: "level1",
level2: "level2",
level2: "level2"
};
public get STATUSES() {
return this._LEVELS;
}
}
Utilisé dans une autre classe:
import { MyClass } from "myclasspath";
class AnotherClass {
private myClass = new MyClass();
tryLevel() {
console.log(this.myClass.STATUSES.level1);
}
}
Il suffit simplement de "exporter" variable et "importer" dans votre classe
export var GOOGLE_API_URL = 'https://www.googleapis.com/admin/directory/v1';
// default err string message
export var errStringMsg = 'Something went wrong';
Maintenant, utilisez-le comme
import appConstants = require('../core/AppSettings');
console.log(appConstants.errStringMsg);
console.log(appConstants.GOOGLE_API_URL);