TypeScript'te genel statik sabit


186

TypeScript'te genel statik sabitler gibi bir şey var mı? Ben benzeyen bir sınıf var:

export class Library {
  public static BOOK_SHELF_NONE: string = "None";
  public static BOOK_SHELF_FULL: string = "Full";
}

Bu sınıfta yapabilirim Library.BOOK_SHELF_NONEve tsc şikayet etmiyor. Ama sınıf kütüphanesini başka bir yerde kullanmaya çalışırsam ve aynı şeyi yapmaya çalışırsam, onu tanımıyor demektir.


1
Bu sınıfı başka bir yerde kullanma örneğini gösterebilir misiniz?
raina77ow

@ raina77ow Nasıl kullanacağım için kod eklemeye başladığımda, bunun tsc'nin şikayet ettiği ad alanı nedeniyle olduğunu fark ettim. Eğer cevabı bir cevap olarak vermek istiyorsanız, onu bulmamı sağladığınızdan tamamlandı olarak işaretleyebilirim. Kendi sorularıma cevap vermekten gerçekten hoşlanmıyorum.
Kristal

Ah, nedeni buydu. ) Yardımcı oldum sevindim; Derleme sonuçlarını gösteren yanıtı ekleyeceğim.
raina77ow

1
Ayrıca readonly, bu alanları yeniden atamaya çalıştığınızda TypeScript'in sizi uyaracağından emin olmak için de ekleyeceğim :public static readonly BOOK_SHELF_FULL
Jan Molak

Yanıtlar:


26

Bu TS snippet'inin ( TS Playground aracılığıyla ) derlendiği şey :

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

Gördüğünüz gibi public static, olarak tanımlanan her iki özellik de dışa aktarılan işleve (özellikleri olarak) eklenir; bu nedenle, işlevin kendisine doğru bir şekilde eriştiğiniz sürece erişilebilir olmalıdır.


430

Modern tarayıcılarda daha fazla statik sabit değer gibi davranan bir şey istiyorsanız ( sınıfa diğer kodla değiştirilememesi nedeniyle), sınıfa getyalnızca bir erişimci ekleyebilirsiniz Library(bu yalnızca ES5 + tarayıcıları ve NodeJS için çalışır) :

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);

Çalıştırırsanız, BOOK_SHELF_NONEözelliği yeni bir değere ayarlama girişiminin işe yaramadığını görürsünüz .

2.0

TypeScript 2.0'da, readonlyçok benzer sonuçlar elde etmek için kullanabilirsiniz :

export class Library {
    public static readonly BOOK_SHELF_NONE = "None";
    public static readonly BOOK_SHELF_FULL = "Full";
}

Sözdizimi biraz daha basit ve daha açıktır. Bununla birlikte, derleyici çalışma süresinden ziyade değişiklikleri önler (ilk örnekte olduğu gibi, değişikliğin gösterildiği gibi kullanılmasına izin verilmez).


26
bu çok iyi bir cevap. kodlamam bundan dolayı daha mutlu.
gonzofish

7
Ancak bu çözümün bir dezavantajı vardır - bu dinamik değerleri tip kısıtı olarak kullanamazsınız , örneğin:type MyType: Library.BOOK_SHELF_NONE | Library.BOOK_SHELF_FULL;
Kostiantyn Kolesnichenko

2
@AdrianMoisa - lütfen yeni bir soru sorun.
WiredPrairie

2
@ hex-lütfen yeni bir soru sorun.
WiredPrairie

1
Numaralandırmalar ne olacak?
meraklı95

40

Bunu aşağıdaki gibi ad alanlarını kullanarak yapabilirsiniz :

export namespace Library {
    export const BOOK_SHELF_NONE: string = 'NONE';
}

Ardından başka bir yerden içe aktarabilirsiniz:

import {Library} from './Library';
console.log(Library.BOOK_SHELF_NONE);

Orada da bir sınıfa ihtiyacınız varsa bu alanı ad alanının içine ekleyin: export class Book {...}


2
Bir nesnenin başlatılmasında sabitleri kullanıyorsam, bunlar tanınmıyor gibi görünüyor. Örneğin: { type: Library.BOOK_SHELF_NONE }ad alanının çağrıldığı herhangi bir dışa aktarma olmadığını düşünüyor gibi görünüyor BOOK_SHELF_NONE. Ancak, ben sadece başvuru ile yerel bir değişken ayarlarsanız, o iyi giderir. TS 2.2
ezmek

13

Bu arada bu bir dekoratörle birlikte çözülebilir Object.freezeveya Object.definePropertybunu kullanıyorum, tonlarca alıcı kullanmaktan biraz daha güzel. Eylem halinde görmek için bunu doğrudan TS Playground kopyalayabilir / yapıştırabilirsiniz . - İki seçenek var


Alanları tek tek "sonlandır"

Aşağıdaki dekoratör hem açıklamalı statik hem de statik olmayan alanları "salt alıcı özellikleri" ne dönüştürür.

Not : Başlangıç ​​değeri olmayan bir örnek değişkenine açıklama eklenirse @final, ilk atanan değer (ne olursa olsun) son değer olur.

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

Dekoratör: Bunu kodunuza eklediğinizden emin olun!

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

Yukarıdaki dekoratöre bir alternatif olarak, bunun katı bir versiyonu da olacaktır, bu da birisi "use strict";ayarlanmakta olan alana bir değer atamaya çalıştığında bir Hata bile atacaktır . (Bu sadece statik kısım)

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

Her statik alanı "sonlandır"

Olası Dezavantaj: Bu yalnızca o sınıfın TÜM statik değerleri için veya hiçbiri için kullanılamaz, ancak belirli statik değerlere uygulanamaz.

/**
* 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."

Bence bu en temiz yaklaşım. Teşekkürler!
Thibs

Küçük bir ekleme: statik değerlerin veya if (!value)olması if (value === undefined)için olmalıdır . ""0
Leon Adler

6

Teşekkürler WiredPrairie!

Cevabınızı biraz genişletmek için, burada bir sabitler sınıfı tanımlamanın tam bir örneği.

// CYConstants.ts

class CYConstants {
    public static get NOT_FOUND(): number    { return -1; }
    public static get EMPTY_STRING(): string { return ""; }
}

export = CYConstants;

Kullanmak

// main.ts

import CYConstants = require("./CYConstants");

console.log(CYConstants.NOT_FOUND);    // Prints -1
console.log(CYConstants.EMPTY_STRING); // Prints "" (Nothing!)

2

Aşağıdaki çözüm TS 1.7.5'ten itibaren de geçerlidir.

// Constancts.ts    
export const kNotFoundInArray = -1;
export const AppConnectionError = new Error("The application was unable to connect!");
export const ReallySafeExtensions = ["exe", "virus", "1337h4x"];

Kullanmak:

// Main.ts    
import {ReallySafeExtensions, kNotFoundInArray} from "./Constants";

if (ReallySafeExtensions.indexOf("png") === kNotFoundInArray) {
    console.log("PNG's are really unsafe!!!");
}

Bu gerçekten harika, ancak birkaç değerden fazlasını tanımlamak için kullanıyorsanız (diğer dillerde bir Enum'da olduğu gibi), tüm değerleri ayrı ayrı içe aktarmaya devam edersiniz. Bu çok çabuk hantal olabilir!
Corey Larson

% 100 kabul etti. Onları adlandırabilir, ancak içine bakmadım. Belki "./Constants.ts" klasöründen {ArrayConstants} dosyasını içe aktarabilir ve ardından ArrayConstants.NotFound gibi bir şey yapabilirsiniz. Zaman aldığımda onunla daha fazla oynamak zorunda.
Andrew

Evet, değerleri ayrı ayrı kullanmaktan kaçınmak için ad alanlarını kullanabilirsiniz, burada kendi cevabımı okuyabilirsiniz.
Ivan Castellanos

0

Sadece değişkeninizi 'dışa aktar' ve sınıfınızda 'içe aktar'

export var GOOGLE_API_URL = 'https://www.googleapis.com/admin/directory/v1';

// default err string message
export var errStringMsg = 'Something went wrong';

Şimdi,

import appConstants = require('../core/AppSettings');
console.log(appConstants.errStringMsg);
console.log(appConstants.GOOGLE_API_URL);

0

Bir alıcıyı kullanabilirsiniz, böylece mülkünüz yalnızca okuma yapar. Misal:

export class MyClass {
    private _LEVELS = {
        level1: "level1",
        level2: "level2",
        level2: "level2"
    };

    public get STATUSES() {
        return this._LEVELS;
    }
}

Başka bir sınıfta kullanılır:

import { MyClass } from "myclasspath";
class AnotherClass {
    private myClass = new MyClass();

    tryLevel() {
       console.log(this.myClass.STATUSES.level1);
    }
}

Bu, sürekli olarak yazılabilir olması beklenen şeyi yapar:let a = new MyClass(); a.STATUSES.level1 = 'not level1'; let plainWrong = new MyClass().STATUSES.level1; // 'not level1'
Leon Adler
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.