Classi statiche TypeScript


145

Volevo passare a TypeScript dal JS tradizionale perché mi piace la sintassi simile a C #. Il mio problema è che non riesco a scoprire come dichiarare le classi statiche in TypeScript.

In C #, utilizzo spesso le classi statiche per organizzare variabili e metodi, riunendoli in una classe denominata, senza la necessità di installare un oggetto. In Vanilla JS, lo facevo con un semplice oggetto JS:

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

In TypeScript, preferirei il mio approccio C-sharpy, ma sembra che in TS non esistano classi statiche. Qual è la soluzione appropriata per questo problema?

Risposte:


166

TypeScript non è C #, quindi non dovresti necessariamente aspettarti gli stessi concetti di C # in TypeScript. La domanda è: perché vuoi classi statiche?

In C # una classe statica è semplicemente una classe che non può essere sottoclassata e deve contenere solo metodi statici. C # non consente di definire funzioni al di fuori delle classi. In TypeScript questo è possibile, tuttavia.

Se stai cercando un modo per mettere le tue funzioni / metodi in uno spazio dei nomi (cioè non globale), potresti prendere in considerazione l'uso dei moduli di TypeScript, ad es.

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

In modo che sia possibile accedere a Mf () esternamente, ma non a s, e non è possibile estendere il modulo.

Vedere la specifica TypeScript per maggiori dettagli.


quindi un modulo può avere un metodo statico, ma una classe no? ma i moduli non possono avere dati statici. Non è conveniente come JS per il wrapping di dati e codice senza dover istanziare nulla.
dcsan,

Potrebbe essere utile includere che dovrai includere .jsnel tuo html. Quindi Angular 2probabilmente stai usando System... così farebbe System.import("Folder/M");(o qualunque sia il percorso del .jsfile compilato ) prima delbootstrap import
Serj Sagan

11
Questo è deprecato. Inoltre tslint non ti permetterà più di farlo per moduli e spazi dei nomi. Leggi qui: palantir.github.io/tslint/rules/no-namespace
Florian Leitgeb

Ho un caso d'uso per avere classi statiche. In questo momento, ho una classe che contiene solo metodi statici. Nel progetto dobbiamo fornire una sorta di configurazione per ogni classe che può essere istanziata. Dichiarare tale classe come statica non solo mi aiuterebbe a notare che non verrà istanziato, ma eviterebbe anche che altri sviluppatori aggiungano membri di istanza ad esso.
mdarefull,

@florian leitgeb qual è il modo preferito quindi, una classe con solo metodi statici e / o parole chiave astratte? Sembra solo scadente rispetto al modulo che ora sembra deprecato
cablato il

181

Le classi astratte sono state un cittadino di prima classe di TypeScript da TypeScript 1.6. Non è possibile creare un'istanza di una classe astratta.

Ecco un esempio:

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

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

const okay = MyClass.doSomething();

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

6
Quando si ha a che fare con le classi statiche, questa è la risposta migliore e io voto questo. Singletonè per il modello di memoria condivisa della stessa istanza della classe. Inoltre, una classe statica non ha istanza per definizione, quindi è necessario generare un'eccezione se il client tenta di inizializzarla.
Loretoparisi,

1
Possiamo fare una lezione astratta?
KimchiMan

@KimchiMan: sì, la abstractparola chiave è supportata in TypeScript.
Fenton,

1
Aggiornato per l'uso abstract! @KimchiMan - ottima idea!
Obsidian,

3
È un approccio migliore rispetto a contrassegnare il costruttore come privato?
Joro Tenev,

72

La definizione di proprietà e metodi statici di una classe è descritta in 8.2.1 della specifica del linguaggio dattiloscritto :

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

dove Point.distance()è un metodo statico (o "classe").


14
Questo mostra come creare un metodo statico , non risponde alla domanda che riguarda le classi statiche (a meno che la vera domanda non sia effettivamente sui metodi statici).
Marcus,

18
Grazie per il commento. Descrive come creare proprietà e metodi statici, che nel loro insieme consentono di creare una classe con dati e funzionalità senza la necessità di creare istanze. Pur non essendo specificamente una "classe statica", soddisfa i requisiti descritti nell'esempio JavaScript dell'OP.
Rob Raisch,

c # non aveva classi statiche fino alla versione 2. esistono in c # solo per impedirti di crearle. non puoi farlo con JavaScript, quindi non ha molto senso
Simon_Weaver,

4
@Simon_Weaver Non puoi fare cosa in JavaScript? Impedire che le classi vengano istanziate? Il dattiloscritto non si preoccupa molto del runtime, purché si ottenga un errore di compilazione quando si tenta di eseguire operazioni non consentite, questo è tutto ciò di cui abbiamo bisogno.
Alex

Immagino che intendevo dire "non avevamo la PAROLA CHIAVE statica a livello di classe (il che significa che non è possibile creare un'istanza di una classe)" fino alla versione 2. ma rileggendolo penso che il mio commento sia mancato punto comunque. l'OP non stava davvero cercando una "parola chiave statica" per l'intera classe
Simon_Weaver

20

Questa domanda è piuttosto datata, ma volevo lasciare una risposta che sfrutta l'attuale versione della lingua. Sfortunatamente le classi statiche non esistono ancora in TypeScript, tuttavia è possibile scrivere una classe che si comporta in modo simile solo con un piccolo overhead utilizzando un costruttore privato che impedisce l'istanza delle classi dall'esterno.

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

Questo frammento ti permetterà di usare classi "statiche" simili alla controparte C # con l'unico aspetto negativo che è ancora possibile istanziarle dall'interno. Fortunatamente non puoi estendere le classi con costruttori privati.


9

Questo è un modo:

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

__static_ctorecco un'espressione di funzione immediatamente invocata. Typescript genererà il codice per chiamarlo alla fine della classe generata.

Aggiornamento: per i tipi generici nei costruttori statici, ai quali non è più consentito fare riferimento dai membri statici, è necessario un passaggio aggiuntivo ora:

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

In ogni caso, ovviamente, potresti semplicemente chiamare il costruttore generico di tipo statico dopo la classe, come ad esempio:

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

Basta ricordarsi di utilizzare MAI thisa __static_ctorsopra (ovviamente).


In questo modo emette ancora un costruttore per la classe.
Richiama la memoria il

1
"In questo modo" è un trucco e non modifica il normale funzionamento del compilatore, come previsto. class SomeClass {}Genera persino un costruttore - difficilmente vale la pena commentare come se fosse introdotto un nuovo problema. ;) FYI: Non ci sono veri "costruttori" in JS - solo funzioni che hanno un "questo" quando vengono chiamate su oggetti o via new. Ciò esiste indipendentemente da qualsiasi "classe".
James Wilkins,

6

Ho ottenuto lo stesso caso d'uso oggi (31/07/2018) e ho riscontrato che questa è una soluzione alternativa. Si basa sulla mia ricerca e ha funzionato per me. Aspettativa - Per ottenere quanto segue in TypeScript:

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

L'ho fatto:

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

Quindi lo consumeremo come di seguito:

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

Questo ha funzionato per me. Se qualcuno ha altri suggerimenti migliori, per favore fateci sentire tutti !!! Grazie...


3

Esistono classi statiche in linguaggi come C # perché non esistono altri costrutti di livello superiore per raggruppare dati e funzioni. In JavaScript, tuttavia, lo fanno e quindi è molto più naturale dichiarare un oggetto come hai fatto tu. Per imitare più da vicino la sintassi della classe, puoi dichiarare i metodi in questo modo:

const myStaticClass = {
    property: 10,

    method() {

    }
}

1
Questo approccio non confonde il flusso di lavoro? Da un lato usi le classi e le istanze e poi improvvisamente inizi a dichiarare di nuovo i dati nei vecchi oggetti JS normali ... L'uso delle classi statiche aiuta anche la leggibilità, non si tratta solo del funzionamento interno del linguaggio.
Kokodoko,

4
Non lo trovo un casino con il mio flusso di lavoro; al contrario, trovo molto conveniente usare oggetti JavaScrpit senza classe o solo semplici funzioni e costanti in un modulo. Tuttavia, cerco anche di evitare il più possibile lo stato globale, quindi raramente ho bisogno di qualcosa come variabili di classe statiche.
Yogu,

1

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

Questo è un modo per "falsificare" un costruttore statico. Non è privo di pericoli - vedi l' articolo codeplex di riferimento .

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

Un modo possibile per ottenere ciò è disporre di istanze statiche di una classe all'interno di un'altra classe. Per esempio:

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

Quindi è possibile accedere ai parametri utilizzando Wrapper, senza dover dichiararne un'istanza. Per esempio:

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

In questo modo ottieni i vantaggi dell'oggetto di tipo JavaScript (come descritto nella domanda originale) ma con i vantaggi di poter aggiungere la tipizzazione TypeScript. Inoltre, evita di dover aggiungere "statico" davanti a tutti i parametri della classe.


1

Con i moduli esterni ES6 questo può essere ottenuto in questo modo:

// privately scoped array
let arr = [];

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

Ciò impedisce l'uso di moduli interni e spazi dei nomi che è considerato una cattiva pratica da TSLint [1] [2] , consente l'ambito privato e pubblico e impedisce l'inizializzazione di oggetti di classe indesiderati.


0

Stavo cercando qualcosa di simile e mi sono imbattuto in qualcosa chiamato il Singleton Pattern .

Riferimento: Singleton Pattern

Sto lavorando su una classe BulkLoader per caricare diversi tipi di file e volevo usare il modello Singleton per questo. In questo modo posso caricare i file dalla mia classe di applicazione principale e recuperare facilmente i file caricati da altre classi.

Di seguito è riportato un semplice esempio di come creare un score manager per un gioco con TypeScript e il modello Singleton.

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

Quindi, ovunque nelle altre classi, avrai accesso al Singleton:

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

0

Puoi anche utilizzare la parola chiave namespaceper organizzare variabili, classi, metodi e così via. Vedi 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);
        }
    }
}

Vedi il commento di Florian sopra "Questo è deprecato. Inoltre tslint non ti permetterà più di farlo per moduli e spazi dei nomi. Leggi qui: palantir.github.io/tslint/rules/no-namespace"
reggaeguitar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.