const tĩnh công khai trong TypeScript


185

Có một thứ như hằng số tĩnh công khai trong TypeScript không? Tôi có một lớp giống như:

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

Trong lớp đó, tôi có thể làm Library.BOOK_SHELF_NONEvà tsc không phàn nàn. Nhưng nếu tôi cố gắng sử dụng Thư viện lớp ở nơi khác và cố gắng làm điều tương tự, nó sẽ không nhận ra nó.


1
Bạn có thể đưa ra một ví dụ về việc sử dụng lớp đó ở nơi khác không?
raina77ow

@ raina77ow Khi tôi bắt đầu thêm mã cho cách tôi sử dụng nó, tôi nhận ra rằng đó là do không gian tên mà tsc phàn nàn. Nếu bạn muốn đưa nó vào như một câu trả lời tôi có thể đánh dấu nó là hoàn chỉnh vì bạn đã giúp tôi tìm thấy nó. Tôi thực sự không thích trả lời câu hỏi của riêng tôi.
Pha lê

Ah, đó là nguyên nhân. ) Vui mừng tôi đã giúp đỡ; Tôi sẽ thêm câu trả lời hiển thị kết quả tổng hợp.
raina77ow

1
Tôi cũng muốn thêm readonlyđể đảm bảo TypeScript sẽ cảnh báo bạn nếu bạn từng cố gắng gán lại các trường đó:public static readonly BOOK_SHELF_FULL
Jan Molak

Câu trả lời:


26

Đây là những gì đoạn trích TS này được biên dịch (thông qua 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;
});

Như bạn thấy, cả hai thuộc tính được định nghĩa public staticđơn giản là được gắn vào hàm được xuất (như các thuộc tính của nó); do đó chúng nên được truy cập miễn là bạn truy cập đúng chức năng.


428

Nếu bạn thực sự muốn một cái gì đó hoạt động giống như một giá trị không đổi tĩnh trong các trình duyệt hiện đại (trong đó không thể thay đổi bằng mã khác), bạn có thể thêm một trình truy cập getduy nhất vào Librarylớp (điều này sẽ chỉ hoạt động cho các trình duyệt ES5 + và 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);

Nếu bạn chạy nó, bạn sẽ thấy cách cố gắng đặt thuộc BOOK_SHELF_NONEtính thành giá trị mới không hoạt động.

2.0

Trong TypeScript 2.0, bạn có thể sử dụng readonlyđể đạt được kết quả rất giống nhau:

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

Cú pháp đơn giản hơn và rõ ràng hơn. Tuy nhiên, trình biên dịch ngăn các thay đổi thay vì thời gian chạy (không giống như trong ví dụ đầu tiên, trong đó thay đổi sẽ không được phép hoàn toàn như đã trình bày).


26
đây là một câu trả lời tốt mã hóa của tôi bây giờ hạnh phúc hơn vì nó.
gonzofish 6/07/2015

7
Tuy nhiên, giải pháp này có một nhược điểm - bạn không thể sử dụng các giá trị động này làm ràng buộc kiểu, chẳng hạn:type MyType: Library.BOOK_SHELF_NONE | Library.BOOK_SHELF_FULL;
Kostiantyn Kolesnichenko

2
@AdrianMoisa - vui lòng đặt câu hỏi mới.
WiredPrairi

2
@ hex-xin hỏi một câu hỏi mới.
WiredPrairi

1
Thế còn enum?
tò mò95

40

Bạn có thể làm điều đó bằng cách sử dụng các không gian tên , như thế này:

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

Sau đó, bạn có thể nhập nó từ bất cứ nơi nào khác:

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

Nếu bạn cần một lớp ở đó cũng bao gồm nó trong không gian tên: export class Book {...}


2
Nếu tôi đang sử dụng các hằng số trong việc khởi tạo một đối tượng, chúng dường như không được nhận ra. Ví dụ: { type: Library.BOOK_SHELF_NONE }dường như nghĩ rằng không gian tên không có xuất khẩu được gọi BOOK_SHELF_NONE. Tuy nhiên, nếu tôi chỉ đặt một biến cục bộ với tham chiếu, nó sẽ giải quyết nó tốt. TS 2.2
đè bẹp

13

Trong khi đó, điều này có thể được giải quyết thông qua một công cụ trang trí kết hợp với Object.freezehoặc Object.defineProperty, tôi đang sử dụng cái này, nó đẹp hơn một chút so với sử dụng hàng tấn getters. Bạn có thể sao chép / dán trực tiếp TS Playground này để xem nó hoạt động. - Có hai lựa chọn


Làm cho các trường riêng lẻ "cuối cùng"

Trình trang trí sau đây chuyển đổi cả hai trường chú thích tĩnh và không tĩnh thành "thuộc tính chỉ getter".

Lưu ý : Nếu một biến đối tượng không có giá trị ban đầu được chú thích @final, thì giá trị được gán đầu tiên (bất kể khi nào) sẽ là giá trị cuối cùng.

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

Trình trang trí: Hãy chắc chắn rằng bạn bao gồm điều này trong mã của bạn!

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

Thay thế cho trình trang trí ở trên, cũng sẽ có một phiên bản nghiêm ngặt về điều này, thậm chí sẽ gây ra lỗi khi ai đó cố gắng gán một số giá trị cho trường "use strict";khi được đặt. (Đây chỉ là phần tĩnh)

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

Làm cho mọi trường tĩnh "cuối cùng"

Nhược điểm có thể có: Điều này sẽ chỉ hoạt động cho TẤT CẢ các thống kê của lớp đó hoặc không có gì, nhưng không thể áp dụng cho các thống kê cụ thể.

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

Tôi nghĩ rằng đây là cách tiếp cận sạch nhất. Cảm ơn!
Thibs

Một bổ sung nhỏ: if (!value)nên if (value === undefined)để giá trị tĩnh là ""hoặc 0.
Leon Adler

6

Cảm ơn WiredPrairi!

Chỉ cần mở rộng câu trả lời của bạn một chút, đây là một ví dụ hoàn chỉnh về việc xác định một lớp hằng.

// CYConstants.ts

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

export = CYConstants;

Để sử dụng

// main.ts

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

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

2

Giải pháp sau đây cũng hoạt động như 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"];

Để sử dụng:

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

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

Điều này thực sự thú vị, nhưng nếu bạn đang sử dụng nó để xác định nhiều hơn một vài giá trị (như bạn có thể trong Enum bằng các ngôn ngữ khác), thì bạn sẽ bị kẹt khi nhập tất cả các giá trị riêng lẻ. Điều này có thể nhận được rườm rà thực sự nhanh chóng!
Corey Larson

Đồng ý 100%. Có thể có thể đặt tên cho chúng nhưng tôi đã không nhìn vào nó. Có lẽ bạn có thể nhập {ArrayConstants} từ "./Constants.ts" và sau đó làm một cái gì đó như ArrayConstants.NotFound. Phải chơi với nó nhiều hơn khi tôi có thời gian.
Andrew

Vâng, bạn có thể sử dụng không gian tên để tránh sử dụng các giá trị riêng lẻ, đọc câu trả lời của riêng tôi ở đây.
Ivan Castellanos

0

Chỉ cần đơn giản là 'xuất' biến và 'nhập' trong lớp của bạn

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

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

Bây giờ sử dụng nó như là,

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

0

Bạn có thể sử dụng một getter, để tài sản của bạn sẽ chỉ được đọc. Thí dụ:

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

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

Được sử dụng trong lớp khác:

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

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

Điều này làm cho những gì được dự đoán là không thể ghi được:let a = new MyClass(); a.STATUSES.level1 = 'not level1'; let plainWrong = new MyClass().STATUSES.level1; // 'not level1'
Leon Adler
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.