Classes estáticas TypeScript

145

Eu queria passar para o TypeScript a partir do JS tradicional porque gosto da sintaxe semelhante ao c #. Meu problema é que não consigo descobrir como declarar classes estáticas no TypeScript.

Em C #, geralmente uso classes estáticas para organizar variáveis ​​e métodos, reunindo-os em uma classe nomeada, sem a necessidade de instanciar um objeto. No vanilla JS, eu costumava fazer isso com um objeto JS simples:

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

No TypeScript, prefiro minha abordagem C-sharpy, mas parece que as classes estáticas não existem no TS. Qual é a solução apropriada para esse problema?

Rayjax
fonte

Respostas:

166

O TypeScript não é C #, portanto você não deve esperar necessariamente os mesmos conceitos de C # no TypeScript. A questão é por que você quer classes estáticas?

No C #, uma classe estática é simplesmente uma classe que não pode ser subclassificada e deve conter apenas métodos estáticos. O C # não permite definir funções fora das classes. No TypeScript, isso é possível, no entanto.

Se você estiver procurando uma maneira de colocar suas funções / métodos em um espaço para nome (por exemplo, não global), considere usar os módulos do TypeScript, por exemplo

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

Para que você possa acessar Mf () externamente, mas não se você não pode estender o módulo.

Veja a especificação TypeScript para mais detalhes.

Marcus
fonte
então um módulo pode ter um método estático, mas uma classe não pode? mas os módulos não podem ter dados estáticos. Não é tão conveniente quanto o JS para agrupar dados e códigos sem precisar instanciar nada.
dcsan
Pode ser útil incluir que você precisará incluir .jsno seu html. Assim, para Angular 2que você provavelmente está usando System... so'd fazer System.import("Folder/M");(ou qualquer que seja o caminho é para o compilado .jsarquivo) antes dabootstrap import
Serj Sagan
11
Isso está obsoleto. Além disso, o tslint não permitirá mais que você faça isso por módulos e espaços para nome. Leia aqui: palantir.github.io/tslint/rules/no-namespace
Florian Leitgeb
Eu tenho um caso de uso para ter classes estáticas. No momento, tenho uma classe que contém apenas métodos estáticos. No projeto, precisamos fornecer algum tipo de configuração para cada classe que possa ser instanciada. Declarar essa classe como estática não apenas me ajudaria a perceber que ela não será instanciada, mas também evitaria que outros desenvolvedores adicionassem membros da instância a ela.
Mdarefull
@florian leitgeb qual é a maneira preferida, uma classe com métodos estáticos e / ou palavra-chave abstrata? Que apenas parece baixa qualidade em comparação com módulo que agora parece obsoleto
wired00
181

As classes abstratas são cidadãos de primeira classe do TypeScript desde o TypeScript 1.6. Você não pode instanciar uma classe abstrata.

Aqui está um exemplo:

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

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

const okay = MyClass.doSomething();

//const errors = new MyClass(); // Error
Fenton
fonte
6
Ao lidar com classes estáticas, essa é a melhor resposta e eu voto positivo. Singletoné para o padrão de memória compartilhada da mesma instância de classe. Além disso, uma classe estática não possui instância por definição, portanto, você deve lançar uma exceção se o cliente tentar inicializá-la.
Loretoparisi
1
Podemos fazer uma aula abstrata?
perfil completo de KimchiMan
@KimchiMan - sim, a abstractpalavra-chave é suportada no TypeScript.
Fenton
1
Atualizado para usar abstract! @KimchiMan - ótima idéia!
Obsidian
3
Essa é uma abordagem melhor do que marcar o construtor como privado?
Joro Tenev
72

A definição de propriedades e métodos estáticos de uma classe é descrita em 8.2.1 da Especificação de linguagem do tipo de texto :

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

onde Point.distance()é um método estático (ou "classe").

Rob Raisch
fonte
14
Isso mostra como criar um método estático ; ele não responde à pergunta sobre classes estáticas (a menos que a pergunta real seja sobre métodos estáticos).
Marcus
18
Obrigado pelo comentário. Ele descreve como criar propriedades e métodos estáticos, que juntos permitem criar uma classe com dados e funcionalidade sem a necessidade de instanciação. Embora não seja especificamente uma "classe estática", ela atende aos requisitos descritos no exemplo de JavaScript do OP.
Rob Raisch
O c # não tinha classes estáticas até a versão 2. elas existem no c # apenas para impedir que você as instancie. você não pode fazer isso com javascript para que ele não faz muito sentido
Simon_Weaver
4
@Simon_Weaver Você não pode fazer o que em javascript? Impedir que as classes sejam instanciadas? De qualquer forma, o texto datilografado não se importa muito com o tempo de execução, desde que você receba um erro de compilação ao tentar fazer coisas às quais não tem permissão para isso.
Alex
Acho que o que eu quis dizer foi 'não tínhamos a KEYWORD estática no nível da classe (o que significa que você não pode criar uma instância de uma classe)' até a versão 2. mas, lendo-a novamente, acho que meu comentário perdeu o aponte de qualquer maneira. o OP não estava realmente procurando por uma 'palavra-chave estática' para toda a classe
Simon_Weaver
20

Essa pergunta é bastante antiga, mas eu queria deixar uma resposta que aproveite a versão atual do idioma. Infelizmente, as classes estáticas ainda não existem no TypeScript. No entanto, você pode escrever uma classe que se comporte de maneira semelhante, com apenas uma pequena sobrecarga, usando um construtor privado que impede a instanciação de classes de fora.

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

Esse trecho permitirá que você use classes "estáticas" semelhantes à contraparte do C #, com a única desvantagem de que ainda é possível instanciar as mesmas a partir de dentro. Felizmente, embora você não possa estender classes com construtores particulares.

Christian Ivicevic
fonte
9

Esta é uma maneira:

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

__static_ctoraqui está uma expressão de função imediatamente chamada. O texto datilografado produzirá um código para chamá-lo no final da classe gerada.

Atualização: para tipos genéricos em construtores estáticos, que não podem mais ser referenciados por membros estáticos, você precisará de uma etapa extra agora:

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

De qualquer forma, é claro, você poderia chamar o construtor estático do tipo genérico após a classe, como:

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

Basta lembrar de uso NUNCA thisno __static_ctoracima (obviamente).

James Wilkins
fonte
Dessa forma, ainda emite um construtor para a classe.
theycallmemorty
1
"Desta forma" é um hack e não altera a operação normal do compilador, conforme o esperado. Inclusive class SomeClass {}gera um construtor - dificilmente vale a pena comentar como se fosse introduzido um novo problema. ;) FYI: Não existem "construtores" reais em JS - apenas funções que possuem um "this" quando chamados em objetos, ou via new. Isso existe, não importa o que seja para qualquer "classe".
James Wilkins
6

Recebi o mesmo caso de uso hoje (31/07/2018) e achei que isso era uma solução alternativa. É baseado em minha pesquisa e funcionou para mim. Expectativa - Para obter o seguinte no TypeScript:

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

Eu fiz isso:

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

Portanto, consumiremos como abaixo:

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

Isso funcionou para mim. Se alguém tiver outras sugestões melhores, deixe-nos ouvir! Obrigado...

KoRa
fonte
3

Classes estáticas em linguagens como C # existem porque não há outras construções de nível superior para agrupar dados e funções. No JavaScript, no entanto, eles fazem e, portanto, é muito mais natural declarar um objeto como você. Para imitar mais de perto a sintaxe da classe, você pode declarar métodos como este:

const myStaticClass = {
    property: 10,

    method() {

    }
}
Yogu
fonte
1
Essa abordagem não atrapalha o seu fluxo de trabalho? Por um lado, você usa classes e instâncias e, de repente, começa a declarar dados em objetos JS antigos comuns novamente ... O uso de classes estáticas também ajuda na legibilidade, não se trata apenas do funcionamento interno da linguagem.
precisa saber é o seguinte
4
Não acho que isso atrapalhe meu fluxo de trabalho; pelo contrário, acho muito conveniente usar objetos JavaScrpit sem classe ou apenas funções e constantes simples em um módulo. No entanto, também tento evitar o estado global o máximo possível, por isso raramente preciso de algo como variáveis ​​de classe estática.
Yogu
1

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

Essa é uma maneira de "falsificar" um construtor estático. Não é sem seus perigos - veja o item codeplex mencionado .

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
fonte
1

Uma maneira possível de conseguir isso é ter instâncias estáticas de uma classe dentro de outra classe. Por exemplo:

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

Em seguida, os parâmetros podem ser acessados ​​usando o Wrapper, sem ter que declarar uma instância dele. Por exemplo:

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

Portanto, você obtém os benefícios do objeto de tipo JavaScript (conforme descrito na pergunta original), mas com os benefícios de poder adicionar a digitação TypeScript. Além disso, evita a necessidade de adicionar 'estático' na frente de todos os parâmetros da classe.

Steve Roberts
fonte
1

Com os módulos externos ES6, isso pode ser alcançado da seguinte maneira:

// privately scoped array
let arr = [];

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

Isso evita o uso de módulos internos e namespaces, considerados maus práticas pelo TSLint [1] [2] , permite escopo privado e público e evita a inicialização de objetos de classe indesejados.

wizzfizz94
fonte
0

Eu estava procurando por algo semelhante e me deparei com algo chamado Singleton Pattern .

Referência: Singleton Pattern

Estou trabalhando em uma classe BulkLoader para carregar diferentes tipos de arquivos e queria usar o padrão Singleton para isso. Dessa forma, posso carregar arquivos da minha classe principal do aplicativo e recuperar os arquivos carregados facilmente de outras classes.

Abaixo está um exemplo simples de como você pode criar um gerenciador de pontuação para um jogo com TypeScript e o padrão Singleton.

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

Em qualquer lugar de suas outras classes, você teria acesso ao Singleton por:

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

Você também pode usar palavras namespace- chave para organizar suas variáveis, classes, métodos e assim por diante. Veja 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
fonte
Veja o comentário de Florian acima "Isso foi preterido. Além disso, o tslint não permitirá mais que você faça módulos e espaços de nome. Leia aqui: palantir.github.io/tslint/rules/no-namespace"
reggaeguitar