TypeScript static classes

JavascriptClassStaticTypescript

Javascript Problem Overview


I wanted to move to TypeScript from traditional JS because I like the C#-like syntax. My problem is that I can't find out how to declare static classes in TypeScript.

In C#, I often use static classes to organize variables and methods, putting them together in a named class, without needing to instatiate an object. In vanilla JS, I used to do this with a simple JS object:

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

In TypeScript, I would rather go for my C-sharpy approach, but it seems that static classes don't exist in TS. What is the appropriate solution for this problem ?

Javascript Solutions


Solution 1 - Javascript

Abstract classes have been a first-class citizen of TypeScript since TypeScript 1.6. You cannot instantiate an abstract class.

Here is an example:

export abstract class MyClass {    		
    public static myProp = "Hello";
    		
    public static doSomething(): string {
      return "World";
    }
}

const okay = MyClass.doSomething();

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

Solution 2 - Javascript

TypeScript is not C#, so you shouldn't expect the same concepts of C# in TypeScript necessarily. The question is why do you want static classes?

In C# a static class is simply a class that cannot be subclassed and must contain only static methods. C# does not allow one to define functions outside of classes. In TypeScript this is possible, however.

If you're looking for a way to put your functions/methods in a namespace (i.e. not global), you could consider using TypeScript's modules, e.g.

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

So that you can access M.f() externally, but not s, and you cannot extend the module.

See the TypeScript specification for more details.

Solution 3 - Javascript

Defining static properties and methods of a class is described in 8.2.1 of the Typescript Language Specification:

class Point { 
  constructor(public x: number, public y: number) { 
    throw new Error('cannot instantiate using a static class');
  } 
  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); 
  } 
}

where Point.distance() is a static (or "class") method.

UPDATE: The link above has been updated to the most recent available version of the Typescript Specification but please note that per https://github.com/Microsoft/TypeScript/issues/15711 there is no current authoritative specification for Typescript, nor is there expected to be one.

Solution 4 - Javascript

This question is quite dated yet I wanted to leave an answer that leverages the current version of the language. Unfortunately static classes still don't exist in TypeScript however you can write a class that behaves similar with only a small overhead using a private constructor which prevents instantiation of classes from outside.

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

This snippet will allow you to use "static" classes similar to the C# counterpart with the only downside that it is still possible to instantiate them from inside. Fortunately though you cannot extend classes with private constructors.

Solution 5 - Javascript

This is one way:

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

__static_ctor here is an immediately invoked function expression. Typescript will output code to call it at the end of the generated class.

Update: For generic types in static constructors, which are no longer allowed to be referenced by static members, you will need an extra step now:

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 any case, of course, you could just call the generic type static constructor after the class, such as:

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

Just remember to NEVER use this in __static_ctor above (obviously).

Solution 6 - Javascript

I got the same use case today(31/07/2018) and found this to be a workaround. It is based on my research and it worked for me. Expectation - To achieve the following in TypeScript:

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

I did this:

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

Hence we shall consume it as below:

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

This worked for me. If anyone has other better suggestions please let us all hear it !!! Thanks...

Solution 7 - Javascript

Static classes in languages like C# exist because there are no other top-level constructs to group data and functions. In JavaScript, however, they do and so it is much more natural to just declare an object like you did. To more closely mimick the class syntax, you can declare methods like so:

const myStaticClass = {
    property: 10,

    method() {

    }
}

Solution 8 - Javascript

With ES6 external modules this can be achieved like so:

// privately scoped array
let arr = [];

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

This prevents the use of internal modules and namespaces which is considered bad practice by TSLint [1] [2], allows private and public scoping and prevents the initialisation of unwanted class objects.

Solution 9 - Javascript

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

This is a way to 'fake' a static constructor. It's not without its dangers - see the referenced codeplex item.

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

Solution 10 - Javascript

One possible way to achieve this is to have static instances of a class within another class. For example:

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

Then parameters can be accessed using Wrapper, without having to declare an instance of it. For example:

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

So you get the benefits of the JavaScript type object (as described in the original question) but with the benefits of being able to add the TypeScript typing. Additionally, it avoids having to add 'static' in front of all the parameters in the class.

Solution 11 - Javascript

You can use abstract classes with public static readonly members to achieve something very similar to what you're looking for. I believe you're looking for something like a struct from C# or C/C++ to organise small pieces of data together.

The cool thing about abstract classes is that

  • they cannot be instantiated,
  • they can only be derived from and
  • they can provide base implementations for some or all of the methods defined in them.

You can even use this technique to somewhat mimic an enum (you can't switch on them for example) but have properties that can be more than just strings or numbers.

// you can omit the public keyword because it's the default in TS, I left it here for clarity

export abstract class RequestTypes {
  public static readonly All = 'All types';
  public static readonly Partners = 'Partners';
  public static readonly Articles = 'Articles';
}

Solution 12 - Javascript

I was searching for something similar and came accross something called the Singleton Pattern.

Reference: Singleton Pattern

> I am working on a BulkLoader class to load different types of files and > wanted to use the Singleton pattern for it. This way I can load files > from my main application class and retrieve the loaded files easily > from other classes. > > Below is a simple example how you can make a score manager for a game > with TypeScript and the Singleton pattern. > > 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; > } } > > Then anywhere in your other classes you would get access to the > Singleton by: > var scoreManager = SingletonClass.getInstance(); scoreManager.setScore(10); scoreManager.addPoints(1); scoreManager.removePoints(2); console.log( scoreManager.getScore() );

Solution 13 - Javascript

You can also use keyword namespace to organize your variables, classes, methods and so on. See 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);
        }
    }
}

Solution 14 - Javascript

You can create a class in Typescript as follows:

export class Coordinate {
        static x: number;
        static y: number;
        static gradient() {
            return y/x;
    	}
    }

and reference it's properties and methods "without" instantiation so:

Coordinate.x = 10;
Coordinate.y = 10;
console.log(`x of ${Coordinate.x} and y of ${Coordinate.y} has gradient of ${Coordinate.gradient()}`);

Fyi using backticks `` in conjunction with interpolation syntax ${} allows ease in mixing code with text :-)

Solution 15 - Javascript

My preferred method is to just use a const object (mainly instead of enums):

const RequestTypes2 = {
    All: 'All types',
    Partners: 'Partners',
    Articles: 'Articles',
} as const; // need the "const" to force the property types to be string literal types (hover RequestTypes2 to see!)

// now you can do this (hover AllowedRequestTypes to see the inferred type)
type AllowedRequestTypes = typeof RequestTypes2[keyof typeof RequestTypes2];

function doRequest(requestType: AllowedRequestTypes) {

}

// these should work 
doRequest('Partners');
doRequest(RequestTypes2.All);
doRequest(RequestTypes.Articles);   // the property's type is "Articles" (string literal type)

// this fails
doRequest('Incorrect');

Check this TS playground.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRayjaxView Question on Stackoverflow
Solution 1 - JavascriptFentonView Answer on Stackoverflow
Solution 2 - JavascriptMarcusView Answer on Stackoverflow
Solution 3 - JavascriptRob RaischView Answer on Stackoverflow
Solution 4 - JavascriptChristian IvicevicView Answer on Stackoverflow
Solution 5 - JavascriptJames WilkinsView Answer on Stackoverflow
Solution 6 - JavascriptKoRaView Answer on Stackoverflow
Solution 7 - JavascriptYoguView Answer on Stackoverflow
Solution 8 - Javascriptwizzfizz94View Answer on Stackoverflow
Solution 9 - JavascriptSimon_WeaverView Answer on Stackoverflow
Solution 10 - JavascriptSteve RobertsView Answer on Stackoverflow
Solution 11 - JavascriptPaul-SebastianView Answer on Stackoverflow
Solution 12 - JavascriptstoXeView Answer on Stackoverflow
Solution 13 - Javascriptjaguar7021View Answer on Stackoverflow
Solution 14 - JavascriptKieran RyanView Answer on Stackoverflow
Solution 15 - JavascriptSzilveszter SafarView Answer on Stackoverflow