Kelas statis TypeScript

145

Saya ingin pindah ke TypeScript dari JS tradisional karena saya suka sintaks mirip C #. Masalah saya adalah bahwa saya tidak dapat menemukan cara mendeklarasikan kelas statis di TypeScript.

Di C #, saya sering menggunakan kelas statis untuk mengatur variabel dan metode, menempatkan mereka bersama-sama di kelas bernama, tanpa harus membuat instance objek. Di vanilla JS, saya biasa melakukan ini dengan objek JS sederhana:

var myStaticClass = {
    property: 10,
    method: function(){}
}

Dalam TypeScript, saya lebih suka menggunakan pendekatan C-sharpy saya, tetapi tampaknya kelas statis tidak ada di TS. Apa solusi yang tepat untuk masalah ini?

Rayjax
sumber

Jawaban:

166

TypeScript bukan C #, jadi Anda seharusnya tidak mengharapkan konsep yang sama dari C # di TypeScript tentu. Pertanyaannya adalah mengapa Anda ingin kelas statis?

Dalam C # kelas statis hanyalah kelas yang tidak dapat disubklasifikasikan dan hanya boleh berisi metode statis. C # tidak memungkinkan seseorang untuk mendefinisikan fungsi di luar kelas. Namun dalam TypeScript ini dimungkinkan.

Jika Anda mencari cara untuk menempatkan fungsi / metode Anda dalam namespace (yaitu tidak global), Anda dapat mempertimbangkan untuk menggunakan modul TypeScript, misalnya

module M {
    var s = "hello";
    export function f() {
        return s;
    }
}

Sehingga Anda dapat mengakses Mf () secara eksternal, tetapi tidak s, dan Anda tidak dapat memperpanjang modul.

Lihat spesifikasi TypeScript untuk lebih jelasnya.

Marcus
sumber
jadi modul dapat memiliki metode statis, tetapi kelas tidak bisa? tetapi modul tidak dapat memiliki data statis. Ini tidak semudah JS untuk membungkus data dan kode tanpa harus instantiate apapun.
dcsan
Mungkin bermanfaat untuk memasukkan bahwa Anda harus memasukkannya ke .jsdalam html. Jadi untuk Angular 2Anda mungkin menggunakan System... begitu juga melakukan System.import("Folder/M");(atau apa pun jalan ke .jsfile yang dikompilasi ) sebelumbootstrap import
Serj Sagan
11
Ini sudah usang. Tslint juga tidak akan membiarkan Anda melakukannya lagi untuk modul dan ruang nama. Baca di sini: palantir.github.io/tslint/rules/no-namespace
Florian Leitgeb
Saya memiliki kasus penggunaan untuk memiliki kelas statis. Saat ini, saya memiliki kelas yang hanya berisi metode statis. Dalam proyek ini kita perlu menyediakan semacam konfigurasi untuk setiap kelas yang dapat dipakai. Mendeklarasikan kelas seperti statis tidak hanya akan membantu saya memperhatikan bahwa itu tidak akan dipakai tetapi juga menghindari pengembang lain menambahkan anggota contoh ke dalamnya.
mdarefull
@ leorgeb Florida apa cara yang disukai kemudian, kelas dengan hanya metode statis dan / atau kata kunci abstrak? Itu sepertinya jelek dibandingkan dengan modul yang sekarang tampaknya usang
wired00
181

Kelas abstrak telah menjadi warga negara kelas satu dari TypeScript sejak TypeScript 1.6. Anda tidak dapat membuat instance kelas abstrak.

Berikut ini sebuah contoh:

export abstract class MyClass {         
    public static myProp = "Hello";

    public static doSomething(): string {
      return "World";
    }
}

const okay = MyClass.doSomething();

//const errors = new MyClass(); // Error
Fenton
sumber
6
Ketika berhadapan dengan kelas statis, ini adalah respons terbaik dan saya membenarkan ini. Singletonadalah untuk pola memori bersama dari instance kelas yang sama. Juga, kelas statis tidak memiliki instance dengan definisi, jadi Anda harus melempar pengecualian jika klien mencoba untuk menginisialisasi itu.
loretoparisi
1
Bisakah kita membuat kelas abstrak?
KimchiMan
@ KimchiMan - ya, abstractkata kunci didukung dalam TypeScript.
Fenton
1
Diperbarui untuk digunakan abstract! @ KimchiMan - ide bagus!
Obsidian
3
Apakah ini pendekatan yang lebih baik daripada menandai konstruktor sebagai pribadi?
Joro Tenev
72

Mendefinisikan properti dan metode statis suatu kelas dijelaskan dalam 8.2.1 dari Spesifikasi Bahasa Naskah :

class Point { 
  constructor(public x: number, public y: number) { } 
  public distance(p: Point) { 
    var dx = this.x - p.x; 
    var dy = this.y - p.y; 
    return Math.sqrt(dx * dx + dy * dy); 
  } 
  static origin = new Point(0, 0); 
  static distance(p1: Point, p2: Point) { 
    return p1.distance(p2); 
  } 
}

di mana Point.distance()metode statis (atau "kelas").

Rob Raisch
sumber
14
Ini menunjukkan cara membuat metode statis , itu tidak menjawab pertanyaan yang tentang kelas statis (kecuali pertanyaan sebenarnya adalah tentang metode statis).
Marcus
18
Terima kasih atas komentarnya. Ini menjelaskan cara membuat properti dan metode statis, yang secara bersama-sama memungkinkan seseorang untuk membuat kelas dengan data dan fungsionalitas tanpa perlu instantiasi. Meskipun tidak secara khusus "kelas statis", ia memenuhi persyaratan seperti yang dijelaskan oleh contoh JavaScript OP.
Rob Raisch
c # tidak memiliki kelas statis sampai versi 2. mereka ada di c # hanya untuk mencegah Anda membuat instance mereka. Anda tidak dapat melakukannya dengan javascript sehingga tidak masuk akal
Simon_Weaver
4
@Simon_Weaver Anda tidak bisa melakukan apa di javascript? Cegah kelas untuk dipakai? Bagaimanapun juga, naskah tidak terlalu peduli dengan runtime, asalkan Anda mendapatkan kesalahan kompilasi ketika Anda mencoba melakukan hal-hal yang tidak diizinkan untuk itu saja yang kami butuhkan.
Alex
Saya kira apa yang saya maksudkan adalah 'kami tidak memiliki KEYWORD statis di tingkat kelas (yang berarti Anda tidak dapat membuat instance kelas)' hingga versi 2. tetapi membacanya lagi saya pikir komentar saya agak ketinggalan. pokoknya pokoknya. OP tidak benar-benar mencari 'kata kunci statis' untuk seluruh kelas
Simon_Weaver
20

Pertanyaan ini cukup tanggal namun saya ingin meninggalkan jawaban yang memanfaatkan versi bahasa saat ini. Sayangnya kelas statis masih belum ada di TypeScript namun Anda dapat menulis kelas yang berperilaku serupa dengan hanya overhead kecil menggunakan konstruktor pribadi yang mencegah instantiasi kelas dari luar.

class MyStaticClass {
    public static readonly property: number = 42;
    public static myMethod(): void { /* ... */ }
    private constructor() { /* noop */ }
}

Cuplikan ini akan memungkinkan Anda untuk menggunakan kelas "statis" mirip dengan mitra C # dengan satu-satunya downside yang masih memungkinkan untuk membuat instance mereka dari dalam. Untungnya, Anda tidak dapat memperpanjang kelas dengan konstruktor pribadi.

Christian Ivicevic
sumber
9

Ini satu cara:

class SomeClass {
    private static myStaticVariable = "whatever";
    private static __static_ctor = (() => { /* do static constructor stuff :) */ })();
}

__static_ctordi sini adalah ekspresi fungsi yang segera dipanggil. Naskah akan menampilkan kode untuk memanggilnya di akhir kelas yang dihasilkan.

Pembaruan: Untuk tipe umum dalam konstruktor statis, yang tidak lagi diizinkan untuk direferensikan oleh anggota statis, Anda akan memerlukan langkah tambahan sekarang:

class SomeClass<T> {
    static myStaticVariable = "whatever";
    private ___static_ctor = (() => { var someClass:SomeClass<T> ; /* do static constructor stuff :) */ })();
    private static __static_ctor = SomeClass.prototype.___static_ctor();
}

Dalam kasus apa pun, tentu saja, Anda bisa memanggil konstruktor statis tipe generik setelah kelas, seperti:

class SomeClass<T> {
    static myStaticVariable = "whatever";
    private __static_ctor = (() => { var example: SomeClass<T>; /* do static constructor stuff :) */ })();
}
SomeClass.prototype.__static_ctor();

Hanya ingat untuk TIDAK PERNAH menggunakan thisdi __static_ctoratas (jelas).

James Wilkins
sumber
Cara ini masih memancarkan konstruktor untuk kelas.
theycallmemorty
1
"Cara ini" adalah peretasan, dan tidak mengubah operasi normal kompiler, seperti yang diharapkan. Bahkan class SomeClass {}menghasilkan konstruktor - hampir tidak layak dikomentari seolah-olah masalah baru diperkenalkan. ;) FYI: Tidak ada "konstruktor" nyata di JS - hanya fungsi yang memiliki "ini" saat dipanggil pada objek, atau via new. Ini ada tidak peduli apa pun untuk "kelas" apa pun.
James Wilkins
6

Saya mendapat kasus penggunaan yang sama hari ini (31/07/2018) dan menemukan ini sebagai solusinya. Ini berdasarkan penelitian saya dan itu berhasil untuk saya. Harapan - Untuk mencapai yang berikut dalam TypeScript:

var myStaticClass = {
    property: 10,
    method: function(){} 
}

Saya melakukan ini:

//MyStaticMembers.ts
namespace MyStaticMembers {
        class MyStaticClass {
           static property: number = 10;
           static myMethod() {...}
        }
        export function Property(): number {
           return MyStaticClass.property;
        }
        export function Method(): void {
           return MyStaticClass.myMethod();
        }
     }

Maka kami akan mengkonsumsinya seperti di bawah ini:

//app.ts
/// <reference path="MyStaticMembers.ts" />
    console.log(MyStaticMembers.Property);
    MyStaticMembers.Method();

Ini berhasil untuk saya. Jika ada yang punya saran lain yang lebih baik, silakan beri tahu kami semua !!! Terima kasih...

KoRa
sumber
3

Kelas statis dalam bahasa seperti C # ada karena tidak ada konstruksi tingkat atas lainnya untuk mengelompokkan data dan fungsi. Namun, dalam JavaScript, mereka melakukannya dan jadi lebih alami untuk mendeklarasikan objek seperti yang Anda lakukan. Untuk meniru lebih dekat sintaks kelas, Anda dapat mendeklarasikan metode seperti:

const myStaticClass = {
    property: 10,

    method() {

    }
}
Yogu
sumber
1
Bukankah pendekatan ini mengacaukan alur kerja Anda? Di satu sisi Anda menggunakan kelas dan instance, dan kemudian tiba-tiba Anda mulai mendeklarasikan data dalam objek JS biasa lagi ... Menggunakan kelas statis juga membantu keterbacaan, ini bukan hanya tentang cara kerja bagian dalam bahasa.
Kokodoko
4
Saya tidak menemukannya mengacaukan alur kerja saya; sebaliknya, saya merasa sangat nyaman untuk menggunakan objek JavaScrpit tanpa kelas atau hanya fungsi dan konstanta dalam sebuah modul. Namun, saya juga mencoba menghindari memiliki keadaan global sebanyak mungkin, jadi saya jarang membutuhkan sesuatu seperti variabel kelas statis.
Yogu
1

Lihat http://www.basarat.com/2013/04/typescript-static-constructors-for.html

Ini adalah cara untuk 'memalsukan' konstruktor statis. Ini bukan tanpa bahaya - lihat item codeplex yang dirujuk .

class Test {
    static foo = "orig";

    // Non void static function
    static stat() {
        console.log("Do any static construction here");
        foo = "static initialized";
        // Required to make function non void
        return null;
    }
    // Static variable assignment
    static statrun = Test.stat();
}

// Static construction will have been done:
console.log(Test.foo);
Simon_Weaver
sumber
1

Salah satu cara yang mungkin untuk mencapai ini adalah memiliki instance statis dari kelas dalam kelas lain. Sebagai contoh:

class SystemParams
{
  pageWidth:  number = 8270;
  pageHeight: number = 11690;  
}

class DocLevelParams
{
  totalPages: number = 0;
}

class Wrapper
{ 
  static System: SystemParams = new SystemParams();
  static DocLevel: DocLevelParams = new DocLevelParams();
}

Kemudian parameter dapat diakses menggunakan Wrapper, tanpa harus mendeklarasikan instance itu. Sebagai contoh:

Wrapper.System.pageWidth = 1234;
Wrapper.DocLevel.totalPages = 10;

Jadi Anda mendapatkan manfaat dari objek tipe JavaScript (seperti dijelaskan dalam pertanyaan asli) tetapi dengan manfaat bisa menambahkan pengetikan TypeScript. Selain itu, ia menghindari keharusan menambahkan 'statis' di depan semua parameter di kelas.

Steve Roberts
sumber
1

Dengan modul eksternal ES6 ini dapat dicapai seperti:

// privately scoped array
let arr = [];

export let ArrayModule = {
    add: x => arr.push(x),
    print: () => console.log(arr),
}

Ini mencegah penggunaan modul internal dan ruang nama yang dianggap praktik buruk oleh TSLint [1] [2] , memungkinkan pelingkupan privat dan publik dan mencegah inisialisasi objek kelas yang tidak diinginkan.

wizzfizz94
sumber
0

Saya sedang mencari sesuatu yang serupa dan menemukan sesuatu yang disebut Singleton Pattern.

Referensi: Pola Singleton

Saya sedang mengerjakan kelas BulkLoader untuk memuat berbagai jenis file dan ingin menggunakan pola Singleton untuk itu. Dengan cara ini saya dapat memuat file dari kelas aplikasi utama saya dan mengambil file yang dimuat dengan mudah dari kelas lain.

Di bawah ini adalah contoh sederhana bagaimana Anda bisa membuat manajer skor untuk permainan dengan TypeScript dan pola Singleton.

kelas SingletonClass {

private static _instance:SingletonClass = new SingletonClass();

private _score:number = 0;

constructor() {
    if(SingletonClass._instance){
        throw new Error("Error: Instantiation failed: Use SingletonDemo.getInstance() instead of new.");
    }
    SingletonClass._instance = this;
}

public static getInstance():SingletonClass
{
    return SingletonClass._instance;
}

public setScore(value:number):void
{
    this._score = value;
}

public getScore():number
{
    return this._score;
}

public addPoints(value:number):void
{
    this._score += value;
}

public removePoints(value:number):void
{
    this._score -= value;
}   }

Lalu di mana pun di kelas Anda yang lain, Anda akan mendapatkan akses ke Singleton dengan:

var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10); scoreManager.addPoints(1);
scoreManager.removePoints(2); console.log( scoreManager.getScore() );
Devin Stokes
sumber
0

Anda juga dapat menggunakan kata kunci namespaceuntuk mengatur variabel, kelas, metode, dan sebagainya. Lihat doc

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }

    const lettersRegexp = /^[A-Za-z]+$/;
    const numberRegexp = /^[0-9]+$/;

    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }

    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}
jaguar7021
sumber
Lihat komentar Florian di atas "Ini sudah usang. Juga tslint tidak akan membiarkan Anda melakukan itu lagi untuk modul dan ruang nama. Baca di sini: palantir.github.io/tslint/rules/no-namespace"
reggaeguitar