Статичні класи TypeScript


145

Я хотів перейти до TypeScript з традиційного JS, тому що мені подобається синтаксис C # -подобний. Моя проблема полягає в тому, що я не можу дізнатися, як оголосити статичні класи в TypeScript.

У C # я часто використовую статичні класи для впорядкування змінних та методів, об'єднуючи їх у названий клас, не потребуючи встановлення об'єкта. У ванільній JS я робив це з простим об'єктом JS:

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

У TypeScript я б скоріше зайнявся моїм C-гострим підходом, але, схоже, статичних класів у TS не існує. Яке відповідне рішення для цієї проблеми?

Відповіді:


166

TypeScript не є C #, тому не слід сподіватися на однакові поняття C # в TypeScript. Питання в тому, чому ви хочете статичних класів?

У C # статичний клас - це просто клас, який не може бути підкласом і повинен містити лише статичні методи. C # не дозволяє визначати функції поза класами. Однак у TypeScript це можливо.

Якщо ви шукаєте спосіб розмістити свої функції / методи в просторі імен (тобто не глобально), ви можете розглянути можливість використання модулів TypeScript, наприклад

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

Так що ви можете отримати доступ до Mf () зовні, але не s, і ви не можете розширити модуль.

Докладніше див. У специфікації TypeScript .


тож модуль може мати статичний метод, але клас не може? але модулі не можуть мати статичних даних. Це не так зручно, як JS для упаковки даних і коду, не вимагаючи нічого інстанції.
dcsan

Можливо, буде корисно включити те, що вам потрібно буде включити .jsсвоє html. Тож, Angular 2напевно, ви використовуєте System... так би зробили System.import("Folder/M");(або як би не було до компільованого .jsфайлу) доbootstrap import
Serj Sagan

11
Це застаріло. Також tslint не дозволить вам більше цього робити для модулів та просторів імен. Читайте тут: palantir.github.io/tslint/rules/no-namespace
Флоріан Лейтгеб

У мене є кейс використання для статичних класів. Зараз у мене є клас, який містить лише статичні методи. У проекті нам потрібно надати певну конфігурацію для кожного класу, яку можна інстанціювати. Оголошення такого класу статичним не тільки допоможе мені помітити, що він не буде примірником, але й уникне інших розробників, які додають до нього членів екземпляра.
mdarefull

@florian leitgeb, який тоді кращий спосіб - клас із лише статичними методами та / або абстрактним ключовим словом? Це просто здається шаленим у порівнянні з модулем, який зараз здається застарілим
wired00

182

Абстрактні класи були першокласним громадянином TypeScript з TypeScript 1.6. Ви не можете створити абстрактний клас.

Ось приклад:

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

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

const okay = MyClass.doSomething();

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

6
Що стосується статичних класів, це найкраща відповідь, і я це схвалюю. Singleton- для структури спільної пам'яті того ж екземпляра класу. Крім того, статичний клас не має примірника за визначенням, тому ви повинні викинути виняток, якщо клієнт намагається його ініціалізувати.
loretoparisi

1
Чи можемо ми зробити абстрактний клас?
KimchiMan

@KimchiMan - так, abstractключове слово підтримується в TypeScript.
Фентон

1
Оновлено для використання abstract! @KimchiMan - відмінна ідея!
Обсидіан

3
Це кращий підхід, ніж позначення конструктора приватним?
Жоро Тенев

72

Визначення статичних властивостей та методів класу описано в 8.2.1 Специфікації мови Typescript :

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

де Point.distance()є статичний (або "класний") метод.


14
Це показує, як створити статичний метод , він не відповідає на питання, що стосується статичних класів (якщо справжнє питання насправді не стосується статичних методів).
Маркус

18
Дякуємо за коментар У ньому описано, як створювати статичні властивості та методи, що разом у сукупності дозволяє створити клас із даними та функціоналом без необхідності інстанцій. Хоча конкретно не є "статичним класом", він виконує вимогу, як описано в прикладі JavaScript ОП.
Роб Райш

c # не мав статичних класів до версії 2. вони існують в c # лише для того, щоб запобігти їх інстанції. ви не можете цього зробити з javascript, тому це не має особливого сенсу
Simon_Weaver

4
@Simon_Weaver Ви не можете робити те, що у JavaScript? Запобігти заняттям екземпляром? Typescript все одно не сильно піклується про час виконання, доки ви отримуєте помилку компіляції, коли намагаєтесь робити речі, вам заборонено це все, що нам потрібно.
Олексій

Я думаю, що я мав на увазі: «у нас не було статичного KEYWORD на рівні класу (це означає, що ти не можеш створити екземпляр класу)» до версії 2. але, читаючи його ще раз, я думаю, що мій коментар пропустив точка все одно. ОП насправді не шукав «статичного ключового слова» для всього класу
Simon_Weaver

20

Це питання досить застаріле, але я хотів залишити відповідь, яка використовує поточну версію мови. На жаль, статичні класи досі не існують у TypeScript, проте ви можете написати клас, який веде себе подібним лише з невеликим накладним покриттям, використовуючи приватний конструктор, який запобігає інстанціюванню класів ззовні.

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

Цей фрагмент дозволить вам використовувати "статичні" класи, схожі на аналог C #, з єдиним недоліком, який все одно можливо інстанціювати зсередини. На щастя, хоча ви не можете продовжувати заняття з приватними конструкторами.


9

Це один із способів:

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

__static_ctorось вираз функції, що викликається негайно. Typescript видасть код для виклику його в кінці створеного класу.

Оновлення: для загальних типів у статичних конструкторах, на які більше не дозволяється посилатися статичними членами, вам зараз знадобиться додатковий крок:

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

У будь-якому випадку, звичайно, ви можете просто викликати статичний конструктор загального типу після класу, наприклад:

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

Тільки НЕ забудьте використовувати NEVER thisв __static_ctorвище (очевидно).


Цей спосіб все ще випромінює конструктор для класу.
onicallmemorty

1
"Цей шлях" - це злом, і не змінює нормальну роботу компілятора, як очікувалося. Навіть class SomeClass {}генерує конструктор - навряд чи варто коментувати, ніби введено нове питання. ;) FYI: У JS немає справжніх "конструкторів" - лише функції, які мають "це", коли викликаються на об'єктах або через new. Це існує незалежно від того, що стосується будь-якого "класу".
Джеймс Вілкінс

6

Я отримав той самий випадок використання сьогодні (31.07.2018) і визнав це вирішенням. Він заснований на моїх дослідженнях і працював на мене. Очікування - для досягнення наступного в TypeScript:

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

Я зробив це:

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

Отже, ми будемо споживати його як нижче:

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

Це працювало для мене. Якщо у когось є інші кращі пропозиції, будь ласка, давайте всім почути це !!! Дякую...


3

Статичні класи на таких мовах, як C #, тому що немає інших конструкцій верхнього рівня, які б групували дані та функції. Однак у JavaScript це роблять, і тому набагато природніше просто оголосити об’єкт, як ви зробили. Щоб більш чітко імітувати синтаксис класу, ви можете оголосити такі способи:

const myStaticClass = {
    property: 10,

    method() {

    }
}

1
Чи не такий підхід заплутує ваш робочий процес? З одного боку, ви використовуєте класи та екземпляри, а потім раптом знову починаєте оголошувати дані у звичайних старих JS-об'єктах ... Використання статичних класів також сприяє читаемості, мова не йде лише про внутрішню роботу мови.
Кокодоко

4
Я не вважаю, що це псується з моїм робочим процесом; навпаки, мені дуже зручно використовувати в модулі безкласні об’єкти JavaScrpit або просто звичайні функції та константи. Однак я також намагаюся максимально уникати глобального стану, тому мені рідко виникає потреба у чомусь подібному до змінних статичних класів.
Йогу

1

Дивіться http://www.basarat.com/2013/04/typescript-static-constructors-for.html

Це спосіб "підробити" статичний конструктор. Не обходиться без небезпек - дивіться посилається на кодплексний пункт .

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

Одним із можливих способів досягти цього є статичні екземпляри класу в іншому класі. Наприклад:

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

Тоді до параметрів можна отримати доступ за допомогою Wrapper, не оголошуючи його примірник. Наприклад:

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

Таким чином, ви отримуєте переваги об’єкта типу JavaScript (як описано в початковому запитанні), але з перевагами можливості додати введення TypeScript. Крім того, це дозволяє уникнути необхідності додавати "статичний" перед усіма параметрами в класі.


1

За допомогою зовнішніх модулів ES6 цього можна досягти так:

// privately scoped array
let arr = [];

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

Це перешкоджає використанню внутрішніх модулів та просторів імен, що TSLint [1] [2] вважає поганою практикою , дозволяє проводити приватне та публічне проведення масштабів та запобігає ініціалізації небажаних об'єктів класу.


0

Я шукав щось подібне і натрапив на щось зване Singleton Pattern .

Довідка: однотонний візерунок

Я працюю над класом BulkLoader для завантаження файлів різних типів і хотів використовувати для нього шаблон Singleton. Таким чином я можу завантажувати файли з мого основного класу додатків і легко завантажувати завантажені файли з інших класів.

Нижче наводиться простий приклад того, як ви можете зробити менеджер з оцінок для гри з TypeScript і шаблоном Singleton.

клас 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;
}   }

Тоді в будь-якому іншому класі ви отримаєте доступ до Singleton за допомогою:

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

0

Ви також можете використовувати ключові слова namespaceдля організації змінних, класів, методів тощо. Див. Док

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

Дивіться коментар Флоріана вище "Це застаріло. Також tslint не дозволить вам більше робити це для модулів та просторів імен. Читайте тут: palantir.github.io/tslint/rules/no-namespace"
reggaeguitar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.