Is it possible to make constructor static in TypeScript?
Asked Answered
S

6

38

I read about a static constructor in TypeScript and tried it myself but it doesn't work. I want to initialize a static variable by that (the method shall only be called once) but I get the following compiler error:

Error: 'static' modifier cannot appear on a constructor declaration.

Code:

export class DataManagement {
  private static subjects: string[];

  static constructor() {
    DataManagement.subjects = [];
    //some more code here
  }
}
Solenne answered 31/3, 2018 at 15:42 Comment(3)
Why not just initialize the subjects field on class level, eg.: private static subjects: string[] = []? Why would you want to re-initialize it on each constructor call?Abukir
Well, thanks! It does work! So this is crap? github.com/Microsoft/TypeScript/issues/265Solenne
@BalázsÉdes Because that's just an example - of course there is a lot of other code in there as well... But it is unnecessary to share the code... And I don't want to initialize it on each constructor call but only once - because of that a static constructor. Like static initialization blocks in Java.Solenne
M
43

While other languages like C# do have static constructors, TypeScript (and JavaScript) do not have this feature. That said, you can still define a function statically which you invoke yourself. First, let me explain in which cases you might need a static constructor before I go over the options you have.

When to use a static constructor

A static constructor is useful in cases in which you need to calculate the value of a static attribute. If you just want to set an attribute (to a known value), you don't need a static constructor. It can be done like this:

class Example {
    public static info = 123;
    // ...
}

In case you need to compute the value, you have three ways to "simulate" a static constructor. I go over the options below and set the static attribute "info" in each sample.

Option 1: Call an initialize function after the class is declared

In the code below, the function _initialize is statically defined in the class and invoked immediately after the class is declared. Inside the function, this refers to the class, meaning that the keyword can be used to set any static values (like info in the sample below). Note, that it is not possible to make the function private, as it is invoked from outside.

class Example {
    public static info: number;

    public static _initialize() {
        // ...
        this.info = 123;
    }
}
Example._initialize();

Option 2: Directly invoke the function inside the class

The second option is to use a function, which is directly called after its creation inside the class. The function only looks like its part of the class, but has no relation to the class itself (except being defined inside of it), meaning that you cannot use this inside the function.

class Example {
    static info: number;

    private static _initialize = (() => {
        // "this" cannot be used here
        Example.info = 1234;
    })();
}

Alternative: Calculate a single attribute

Based on the same idea as option 2, you can calculate a single attribute by returning the value. This might be handy in cases where you only want to calculate one attribute of the class.

class Example {
    public static info = (() => {
        // ... calculate the value and return it
        return 123;
    })();
}

What to use

If you only want to calculate a single static attribute, you might want to use the last (alternative) approach. In case you need to do more complex calculations or want to set more attributes, I recommend using option 1 if you don't mind the function being public. Otherwise, go with option 2.

Note, that there is an issue in the TypeScript repository, that contains some more discussions regarding option 1 and 2.

Mismate answered 18/7, 2019 at 14:51 Comment(1)
For option #2 in vsCode I get an error: '_initialize' is declared but its value is never read.ts(6133). I have to use // @ts-ignore to suppress this error. Any other suggestions?Outguess
F
23

Note that beginning with TypeScript 4.4, static block initializers are supported... https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-rc/#static-blocks

class Foo {
    static Foo.count = 0;

    // This is a static block:
    static {
        if (someCondition()) {
            Foo.count++;
        }
    }
}
Fluted answered 24/3, 2022 at 20:0 Comment(2)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewIntegration
Typescript is Friggin awesome. They have to compile to javascript, which must make it easier innovate.Bashibazouk
T
3

Symbol() can be used for this, cleanly with restricted public access.

const STATIC_INIT = Symbol(); // gives you a unique identifier

class MyClass {
  public static[STATIC_INIT] = () => {
    // Your static init code here
  }
}

// Call the init once
MyClass[STATIC_INIT]();
Tradition answered 14/10, 2019 at 14:52 Comment(0)
I
1

You can simulate static constructor using static code in your .ts file.

Let's say you have class which serves a single purpose of parsing parameters and merging them with some defaults.

Just add call after you class declaration.

Here is an example:

export class Env {
    private static _args: {}
    static get args() {
        return this._args;
    }

    static _Initialize() {
        // load settings from Environment
        process.argv.forEach(s => console.log(s))
        this._args =  Object.assign({}, defaults, this.parseCmdLine())
    }
}
Env._Initialize();

Example TS Application: https://github.com/v-andrew/ts-template

It works exactly as static constructor with who caveats:

  • It could be called multiple times
  • It could be called by other users of Env

To nullify those issues redefine _Initialize in the end of _Initialize:

        this._Initialize = ()=>{}
Iconology answered 20/9, 2019 at 15:13 Comment(0)
M
0

You are looking for an object :) :

 const dataManagement: { subjects: string[] } = {
   subjects: []
 };

 export { dataManagement };
Mirabel answered 31/3, 2018 at 15:44 Comment(0)
M
-1

So the use of a static constructor is a bit of a misnomer. your are not try to make the constructor method static, but try to create a static instantiation method. It can be named whatever you want. I've used initialize, personally.

You can have your constructor method be essentially blank

constructor() {}

And then have a static initialize method

static initialize(): <type-to-use> { //initialization logic };

usually in the initialize method you want to invoke the constructor with the keyword new, and after that, default your properties.

Mitman answered 26/7, 2018 at 16:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.