216

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 ?

1
  • I would like to note that even going with a literal object like in your JS example, is type-safe and achieves what you want. Commented Sep 5, 2021 at 15:39

15 Answers 15

295

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
6
  • 8
    When dealing with static classes, this is the best response and I upvote this. Singleton is for the shared memory pattern of the same class instance. Also, a static class has no instance by definition, so you must throw an exception if the client tries to initialize it. Commented Jul 28, 2016 at 16:03
  • 6
    Is this a better approach than marking the constructor as private? Commented Feb 19, 2018 at 11:12
  • @JoroTenev please see the updated answer - which is "yes".
    – Fenton
    Commented Feb 19, 2018 at 12:43
  • Also what about the scope of the static variables ? Is it the same as of that of the other oop languages ?
    – leox
    Commented Apr 6, 2019 at 8:35
  • 1
    @GeorgiTenev: Unless you make the constructor private, you can still subclass this class and instantiate it. Commented May 11, 2023 at 6:52
205

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.

6
  • so a module can have a static method, but a class cannot? but modules cannot have static data. It's not as convenient as JS for wrapping data and code without having to instantiate anything.
    – dcsan
    Commented Aug 24, 2015 at 21:23
  • It might be useful to include that you'll need to include the .js in your html. So for Angular 2 you're probably using System... so'd do System.import("Folder/M"); (or whatever the path is to the compiled .js file) before the bootstrap import
    – Serj Sagan
    Commented Jan 9, 2016 at 8:11
  • 24
    This is deprecated. Also tslint won't let you do that anymore for modules and namespaces. Read here: palantir.github.io/tslint/rules/no-namespace Commented Nov 14, 2017 at 13:24
  • 1
    @florian leitgeb what is the preferred way then, a class with only static methods and/or abstract keyword? That just seems crappy compared to module which now seems deprecated
    – wired00
    Commented Oct 18, 2019 at 20:29
  • 1
    it is possible, I would suggest look at stackoverflow.com/a/13222267/5724101 Commented Feb 9, 2020 at 18:52
83

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.

13
  • 26
    This shows how to create a static method, it doesn't answer the question which is about static classes (unless the real question is actually about static methods).
    – Marcus
    Commented Nov 3, 2012 at 19:45
  • 25
    Thanks for the comment. It describes how to create static properties and methods, which taken together allows one to create a class with data and functionality without the need for instantiation. While not specifically a "static class", it does fulfill the requirement as described by the OP's JavaScript example.
    – Rob Raisch
    Commented Nov 3, 2012 at 19:48
  • c# didn't have static classes until version 2. they exist in c# only to prevent you instantiating them. you can't do that with javascript so it doesn't make much sense Commented May 23, 2014 at 1:59
  • 4
    @Simon_Weaver You cant do what in javascript? Prevent classes to be instantiated? Typescript don't care much about runtime anyway, as long as you get a compile error when you try to do stuff you're not allowed to that's all we need.
    – Alex
    Commented Mar 1, 2016 at 16:41
  • I guess what I meant was 'we didn't have the static KEYWORD at the class level (which means you can't create an instance of a class)' until version 2. but reading it again I think my comment kind of missed the point anyway. the OP wasn't really looking for a 'static keyword' for the whole class Commented Mar 1, 2016 at 16:45
31

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.

1
  • 1
    I completely agree that this is how to achieve the result OP is looking for. In C#, you can’t inherit from a static class. If we followed the suggestion from @Fenton, to use an abstract class, we wouldn’t prevent inheritance. Commented Jul 7, 2023 at 18:36
10

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...

9

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

2
  • This way still emits a constructor for the class. Commented Apr 6, 2016 at 18:52
  • 1
    "This way" is a hack, and doesn't change the compiler's normal operation, as expected. Even class SomeClass {} generates a constructor - hardly worth commenting on as though a new issue is introduced. ;) FYI: There are no real "constructors" in JS - only functions that have a "this" when called on objects, or via new. This exists no matter what for any "class". Commented Apr 7, 2016 at 0:37
7

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() {

    }
}
2
  • 1
    Doesn't this approach muddle your workflow? On the one hand you use classes and instances, and then suddenly you start declaring data in regular old JS objects again... Using static classes also aids readabilty, it's not just about the inner workings of the language.
    – Kokodoko
    Commented Apr 19, 2017 at 11:41
  • 6
    I don't find it messing with my workflow; on the contrary, I find it very convenient to use classless JavaScrpit objects or just plain functions and constants in a module. However, I also try to avoid having global state as much as possible, so I rarely have a need for something like static class variables.
    – Yogu
    Commented Apr 20, 2017 at 7:23
3

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.

0
1

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

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.

1

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';
}
2
1

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.

0

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

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);
        }
    }
}
1
  • See Florian's comment above "This is deprecated. Also tslint won't let you do that anymore for modules and namespaces. Read here: palantir.github.io/tslint/rules/no-namespace" Commented Sep 10, 2018 at 15:57
0

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

1
  • Or you could export an abstract class, which is viable in TypeScript. Add static readonly members and you basically have the equivalent of a static C# class. Commented Jul 5, 2021 at 13:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.