208

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 ?

Rayjax
  • 7,494
  • 11
  • 56
  • 82

15 Answers15

288

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
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • 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. – loretoparisi Jul 28 '16 at 16:03
  • 5
    Is this a better approach than marking the constructor as private? – Georgi Tenev Feb 19 '18 at 11:12
  • @JoroTenev please see the updated answer - which is "yes". – Fenton Feb 19 '18 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 Apr 06 '19 at 08:35
  • This is a great use of the side effect of the abstract keyword. When using, I'd add a comment to your codebase to state that the intention is to make the class static, as opposed to making it abstract – Hoppe Jan 18 '23 at 13:57
  • 1
    @GeorgiTenev: Unless you make the constructor private, you can still subclass this class and instantiate it. – O. R. Mapper May 11 '23 at 06:52
203

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.

Fabio Milheiro
  • 8,100
  • 17
  • 57
  • 96
Marcus
  • 5,987
  • 3
  • 27
  • 40
  • 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 Aug 24 '15 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 Jan 09 '16 at 08:11
  • 24
    This is deprecated. Also tslint won't let you do that anymore for modules and namespaces. Read here: https://palantir.github.io/tslint/rules/no-namespace/ – Florian Leitgeb Nov 14 '17 at 13:24
  • I have an use-case for having static classes. Right now, I have a class that only contains static methods. In the project we need to provide some sort of configuration for each class that can be instantiated. Declaring such class as static would not only help me notice that it won't be instantiated but also avoid other developers adding instance members to it. – mdarefull Feb 16 '18 at 15:48
  • 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 Oct 18 '19 at 20:29
  • 1
    it is possible, I would suggest look at https://stackoverflow.com/a/13222267/5724101 – mojmir.novak Feb 09 '20 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.

Rob Raisch
  • 17,040
  • 4
  • 48
  • 58
  • 25
    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 Nov 03 '12 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 Nov 03 '12 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 – Simon_Weaver May 23 '14 at 01: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 Mar 01 '16 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 – Simon_Weaver Mar 01 '16 at 16:45
  • This answer conflates the concepts of class instantiation and static classes IMOH. Here we first instantiate the class and then access from within it a static method.. this doesn't answer the original question. – Kieran Ryan Dec 11 '20 at 18:44
  • @KieranRyan The original question first described the OP's need for static classes and then asked "What is the appropriate solution for this problem ?" In the absence of Typescript Static Classes, I feel my answer is "the appropriate solution" given the constraints of the language. – Rob Raisch Jan 07 '21 at 20:29
  • @Rob Raisch: If you haven't already please see my answer for a fuller context of my thinking :-) – Kieran Ryan Feb 04 '21 at 16:55
  • @KieranRyan Yup, but, as others have noted of my answer, you are also not creating a static class according to Microsoft's definition. This is easily observed when you attempt to create an instance of your `coordinate`class (which, by established convention is incorrectly named as it does not start with a capital letter) and that instantiation succeeds. According to MSFT C#, "a static class cannot be instantiated". In the example code I provided above, there is no requirement that one call the class constructor; it is there only to illustrate that instantiation accomplishes nothing. – Rob Raisch Feb 19 '21 at 15:34
  • @KieranRyan Upon further reflection, I should have modified my example to throw an exception when one attempts to instantiate a new Point. I have updated my answer to do that. Thanks – Rob Raisch Feb 19 '21 at 15:36
  • I don't agree that class naming conventions other than 'capital first letter' constitute an incorrectly named class. That is pure bias and is simply not fact. The best practice is to consistently leverage standards (of any design so long as all of the code of a project follows it, making it easier to follow/read). Typescript/JavaScript allow for this freedom - and if the compiler says its legal and correct, you have no right to say it isn't; unless you're grading/reviewing code checked against your own standard, that you own and paid the dev to write accordingly. – Rik Mar 16 '21 at 22:24
  • Link no longer works; could you please update the link – user3613932 Apr 14 '22 at 04:32
  • @user3613932 done, but with the mentioned caveat. – Rob Raisch Apr 19 '22 at 20:23
30

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.

Christian Ivicevic
  • 10,071
  • 7
  • 39
  • 74
  • 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. – Frederik Krautwald Jul 07 '23 at 18:36
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).

James Wilkins
  • 6,836
  • 3
  • 48
  • 73
  • This way still emits a constructor for the class. – theycallmemorty Apr 06 '16 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". – James Wilkins Apr 07 '16 at 00:37
9

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

KoRa
  • 101
  • 1
  • 4
6

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

    }
}
Yogu
  • 9,165
  • 5
  • 37
  • 58
  • 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 Apr 19 '17 at 11:41
  • 5
    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 Apr 20 '17 at 07: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.

wizzfizz94
  • 1,288
  • 15
  • 20
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);
senshin
  • 10,022
  • 7
  • 46
  • 59
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
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.

Steve Roberts
  • 2,701
  • 1
  • 15
  • 9
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';
}
Paul-Sebastian Manole
  • 2,538
  • 1
  • 32
  • 33
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() );
stoXe
  • 313
  • 6
  • 13
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);
        }
    }
}
jaguar7021
  • 11
  • 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" – reggaeguitar Sep 10 '18 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 :-)

Kieran Ryan
  • 593
  • 2
  • 8
  • 18
  • 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. – Paul-Sebastian Manole Jul 05 '21 at 13:49