TypeScript statik sınıfları


146

C # benzeri sözdizimini sevdiğim için geleneksel JS'den TypeScript'e geçmek istedim. Benim sorunum statik sınıfları TypeScript bildirmek nasıl bulamıyorum olmasıdır.

C # 'da, değişkenleri ve yöntemleri düzenlemek için bir nesneyi oluşturmak zorunda kalmadan, bunları adlandırılmış bir sınıfta bir araya getirmek için genellikle statik sınıfları kullanırım. Vanilya JS'de bunu basit bir JS nesnesiyle yapardım:

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

TypeScript'te, C-keskin yaklaşımımı tercih ederim, ancak TS'de statik sınıfların mevcut olmadığı anlaşılıyor. Bu sorun için uygun çözüm nedir?

Yanıtlar:


166

TypeScript C # değildir, bu nedenle TypeScript'te aynı C # kavramlarını mutlaka beklememelisiniz. Soru şu: Neden statik sınıflar istiyorsunuz?

C # 'da statik bir sınıf basitçe alt sınıflara ayrılamayan bir sınıftır ve sadece statik yöntemler içermelidir. C #, sınıfların dışındaki işlevlerin tanımlanmasına izin vermez. Ancak TypeScript'te bu mümkündür.

İşlevlerinizi / yöntemlerinizi bir ad alanına (yani global değil) koymak için bir yol arıyorsanız, TypeScript modüllerini kullanmayı düşünebilirsiniz;

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

Böylece Mf () öğesine harici olarak erişebilirsiniz, ancak s ile erişemezsiniz ve modülü genişletemezsiniz.

Daha fazla ayrıntı için TypeScript spesifikasyonuna bakın.


böylece bir modül statik bir yöntem olabilir, ama bir sınıf olamaz? ancak modüllerin statik verileri olamaz. Herhangi bir şey başlatmak zorunda kalmadan veri ve kod kaydırmak için JS kadar kullanışlı değildir.
dcsan

Eklemek gerekeceğini dahil etmek yararlı olabilir .jsGözlerinde farklı html. Yani için Angular 2muhtemelen kullandığınız System... do so'd System.import("Folder/M");(veya yol derlenmiş için ne olursa olsun .jsdosyası) öncebootstrap import
Serj Sagan

11
Bu kullanımdan kaldırıldı. Ayrıca tslint artık modüller ve ad alanları için bunu yapmanıza izin vermeyecektir. Burada okuyun: palantir.github.io/tslint/rules/no-namespace
Florian Leitgeb

Statik sınıflar için bir kullanım durumu var. Şu anda, sadece statik yöntemler içeren bir sınıf var. Projede, her sınıf için somutlaştırılabilecek bir tür yapılandırma sağlamamız gerekiyor. Bu sınıfı statik olarak bildirmek, yalnızca somutlaştırılmayacağını fark etmeme yardımcı olmakla kalmaz, aynı zamanda diğer geliştiricilerin buna örnek üyeleri eklemesini de önler.
mdarefull

@florian leitgeb o zaman tercih edilen yol nedir, sadece statik yöntemlere ve / veya soyut anahtar kelimeye sahip bir sınıf? Artık kullanımdan kaldırılmış gibi görünen modüle kıyasla bok gibi görünüyor
wired00

182

Soyut sınıflar, TypeScript 1.6'dan beri birinci sınıf bir TypeScript vatandaşı olmuştur. Soyut bir sınıfı başlatamazsınız.

İşte bir örnek:

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

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

const okay = MyClass.doSomething();

//const errors = new MyClass(); // Error

6
Statik sınıflarla uğraşırken, bu en iyi yanıttır ve bunu onaylarım. Singletonaynı sınıf örneğinin paylaşılan bellek modeli içindir. Ayrıca, statik sınıfın tanım gereği bir örneği yoktur, bu nedenle istemci sınıfı başlatmaya çalışırsa bir istisna atmanız gerekir.
loretoparisi

1
Soyut bir sınıf yapabilir miyiz?
KimchiMan

@KimchiMan - evet, abstractanahtar kelime TypeScript'te desteklenir.
Fenton

1
Kullanmak için güncellendi abstract! @KimchiMan - harika bir fikir!
Obsidiyen

3
Bu, kurucuyu özel olarak işaretlemekten daha iyi bir yaklaşım mı?
Joro Tenev

72

Bir sınıfın statik özelliklerini ve yöntemlerini tanımlama, Typcript Dil Spesifikasyonu'nun 8.2.1'inde açıklanmaktadır :

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

burada Point.distance()statik (veya "sınıf") bir yöntemdir.


14
Bu, statik bir yöntemin nasıl oluşturulacağını gösterir , statik sınıflarla ilgili soruya cevap vermez (gerçek soru aslında statik yöntemlerle ilgili değilse).
Marcus

18
Yorum için teşekkürler. Birlikte ele alınan statik özelliklerin ve yöntemlerin nasıl oluşturulacağını açıklar, bu da bir örneğe, bir örneğe gerek olmadan veri ve işlevsellik ile bir sınıf oluşturma olanağı verir. Özellikle bir "statik sınıf" olmasa da, OP'nin JavaScript örneğinde açıklandığı gibi bu gereksinimi karşılar.
Rob Raisch

c # sürüm 2'ye kadar statik sınıflar yoktu. c # 'da yalnızca onları örneklemenizi önlemek için varlar. javascript ile bunu yapamazsınız, bu yüzden pek mantıklı değil
Simon_Weaver

4
@Simon_Weaver Javascript'te ne yapamazsınız? Sınıfların somutlaştırılmasını engelliyor musunuz? Daktilo, çalışma süresine çok fazla önem vermiyor, sürece ihtiyacımız olan tek şey, izin verilmeyen şeyler yapmaya çalıştığınızda derleme hatası alırsınız.
Alex

Demek istediğim, sürüm 2'ye kadar 'sınıf düzeyinde statik KEYWORD'ümüz yoktu (yani bir sınıfın örneğini oluşturamazsınız)' idi ama tekrar okuduğumda yorumumun yine de nokta. OP gerçekten tüm sınıf için 'statik bir anahtar kelime' aramıyordu
Simon_Weaver

20

Bu soru oldukça eskidir, ancak dilin mevcut sürümünü kullanan bir cevap bırakmak istedim. Ne yazık ki, statik sınıflar TypeScript'te hala mevcut değildir, ancak sınıfların dışarıdan başlatılmasını önleyen özel bir kurucu kullanarak yalnızca küçük bir ek yüke benzer bir sınıf yazabilirsiniz.

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

Bu snippet, C # muadiline benzer "statik" sınıfları, bunları içeriden somutlaştırmanın tek dezavantajı ile kullanmanıza izin verecektir. Neyse ki özel kurucularla sınıfları genişletemezsiniz.


9

Bu bir yol:

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

__static_ctorhemen çağrılan bir işlev ifadesidir. Typcript, üretilen sınıfın sonunda onu çağırmak için kod çıktısı verecektir.

Güncelleme: Artık statik üyeler tarafından referans alınmasına izin verilmeyen statik kuruculardaki genel türler için, şimdi fazladan bir adıma ihtiyacınız olacak:

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

Her durumda, elbette, sadece genel tür statik yapıcısı diyebiliriz sonra gibi sınıf,:

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

Sadece ASLA kullanımına hatırlamak thisiçinde __static_ctor(belli ki) üzerindedir.


Bu şekilde hala sınıf için bir yapıcı yayar.
theycallmemorty

1
"Bu şekilde" bir saldırıdır ve derleyicinin normal çalışmasını beklendiği gibi değiştirmez. Hatta class SomeClass {}yeni bir konu tanıtıldı sanki yorumlarken pek değerinde - bir kurucu üretir. ;) FYI: JS'de gerçek bir "kurucu" yoktur - yalnızca nesnelerde veya üzerinden çağrıldığında "bu" olan işlevler new. Herhangi bir "sınıf" için ne olursa olsun bu söz konusudur.
James Wilkins

6

Bugün aynı kullanım durumunu aldım (31/07/2018) ve bunun geçici bir çözüm olduğunu gördüm. Benim araştırmamı temel alıyor ve benim için çalıştı. Beklenti - TypeScript'te aşağıdakileri başarmak için:

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

Bunu ben yaptım:

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

Bu nedenle aşağıdaki şekilde tüketeceğiz:

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

Bu benim için çalıştı. Herkes başka daha iyi önerileri varsa lütfen hepimiz duymak izin !!! Teşekkürler...


3

Verileri ve işlevleri gruplamak için başka üst düzey yapı olmadığı için C # gibi dillerde statik sınıflar vardır. Bununla birlikte, JavaScript'te yaparlar ve bu yüzden sizin gibi bir nesneyi beyan etmek çok daha doğaldır. Sınıf sözdizimini daha yakından taklit etmek için aşağıdaki gibi yöntemler bildirebilirsiniz:

const myStaticClass = {
    property: 10,

    method() {

    }
}

1
Bu yaklaşım iş akışınızı karıştırmıyor mu? Bir yandan sınıfları ve örnekleri kullanırsınız ve sonra aniden normal eski JS nesnelerinde veri bildirmeye başlarsınız ... Statik sınıfları kullanmak da okunabilirliğe yardımcı olur, bu sadece dilin iç işleyişi ile ilgili değildir.
Kokodoko

4
İş akışımla uğraştığını görmüyorum; aksine, bir modülde sınıfsız JavaScrpit nesneleri veya sadece düz fonksiyonlar ve sabitler kullanmayı çok uygun buluyorum. Ancak, mümkün olduğunca küresel duruma sahip olmaktan kaçınmaya çalışıyorum, bu yüzden nadiren statik sınıf değişkenleri gibi bir şeye ihtiyacım var.
Yogu

1

Bkz. Http://www.basarat.com/2013/04/typescript-static-constructors-for.html

Bu, statik bir yapıcıyı 'taklit etmenin' bir yoludur. Tehlikeleri olmadan değil - referans verilen codeplex öğesine bakın .

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

1

Bunu başarmanın olası bir yolu, başka bir sınıf içinde bir sınıfın statik örneklerine sahip olmaktır. Örneğin:

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

Daha sonra parametrelere Wrapper kullanılarak bir örneğini bildirmeksizin erişilebilir. Örneğin:

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

Böylece JavaScript yazım nesnesinin avantajlarını (orijinal soruda açıklandığı gibi) elde edersiniz, ancak TypeScript yazmayı ekleyebilmenin faydaları vardır. Ayrıca, sınıftaki tüm parametrelerin önüne 'statik' eklemek zorunda kalmaz.


1

ES6 harici modüller ile bu şekilde elde edilebilir:

// privately scoped array
let arr = [];

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

Bu, TSLint [1] [2] tarafından kötü uygulama olarak kabul edilen dahili modüllerin ve ad alanlarının kullanılmasını önler, özel ve genel kapsam belirlemeye izin verir ve istenmeyen sınıf nesnelerinin başlatılmasını önler.


0

Benzer bir şey arıyordum ve Singleton Pattern .

Referans: Tekli Desen

Farklı dosya türleri yüklemek için bir BulkLoader sınıfında çalışıyorum ve bunun için Singleton desenini kullanmak istedim. Bu şekilde ana uygulama sınıfımdan dosya yükleyebilir ve yüklenen dosyaları diğer sınıflardan kolayca alabilirim.

Aşağıda, TypeScript ve Singleton deseni ile bir oyun için nasıl puan yöneticisi yapabileceğinize dair basit bir örnek verilmiştir.

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

Daha sonra diğer sınıflarınızın herhangi bir yerinde Singleton'a şu şekilde erişebilirsiniz:

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

0

Ayrıca namespacedeğişkenlerinizi, sınıflarınızı, yöntemlerinizi vb. Düzenlemek için anahtar kelime kullanabilirsiniz . Bkz. 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);
        }
    }
}

Florian'ın yukarıdaki yorumuna bakın "Bu kullanımdan kaldırılmıştır. Ayrıca tslint artık modüller ve ad alanları için bunu yapmanıza izin vermeyecektir. Burayı okuyun: palantir.github.io/tslint/rules/no-namespace"
reggaeguitar
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.