A static constructor is a function that is run only once when the class is loaded. It can be used to initialize static class members and maybe as entry point to the application.
Suggested syntax is:
class MyClass {
static initialized = false;
static constructor() {
MyClass.initialized = true;
}
}
alert(MyClass.initialized);
That would be same as the following:
class MyClass {
static initialized = false;
static __ctor = (() => {
MyClass.initialized = true;
})();
}
alert(MyClass.initialized);
Except that __ctor
does not need to be assigned to the class in the output code, and only needs to be invoked in an anonymous function. Also compiler can check that no more than one static constructor is defined in a class.
Update: Since this generates an Immediately-Invoked Function Expression (IIFE), compiler should make sure to move it to the end of the class definition in the output JavaScript.
TypeScript:
class MyClass {
static constructor() {
MyClass.initialized = true;
}
static initialized = false;
}
Expected JavaScript output:
var MyClass = (function () {
function MyClass() {
}
MyClass.initialized = false;
// run last
(function () {
MyClass.initialized = true;
})();
return MyClass;
})();
Note that body of the static constructor is moved to the bottom and invoked.
Related suggestion in codeplex: Please add static constructors to classes
:+1:
You can already accomplish this with a merged class/module:
class MyClass {
static initialize() {
// Initialization
}
}
module MyClass {
MyClass.initialize();
}
Or, for that matter, you can just have the call to initialize immediately follow the class declaration:
class MyClass {
static initialize() {
// Initialization
}
}
MyClass.initialize();
Pleasure hearing from Mr. Hejlsberg...
The only limitation I see there is that you can't make it _private_, whereas in my example you can :)
:+1: for class/module merge suggestion
The merged class/module code requires that the _developer_ remember to call MyClass.initialise()
.
The suggestion I believe is that given the following:
class MyClass {
static initialized = false;
static constructor() {
MyClass.initialized = true;
}
}
We get JavaScript
var MyClass = (function () {
function MyClass() {
}
MyClass.initialized = false;
MyClass.__ctor = (function () {
MyClass.initialized = true;
})();
return MyClass;
})();
@NoelAbrahams Yes, good point about having to remember to call it in the merged class/module pattern.
However, @ahejlsberg's code has the benefit that call to the static method is made at the very end when the class is completely loaded. I updated the suggestion above to reflect that. The static constructor syntax should only generate an anonymous function invocation at the end of the JavaScript class output.
Privacy is a good point here, but it's worth pointing out that you can be a little "evil" to achieve the same goal without having to modify the language. ;)
class C {
private static initialize(): void {
// do stuff
C.initialize = undefined;
}
}
(<any>C).initialize();
You can even replace C.initialize = undefined
with delete C.initialize
if you're afraid someone will iterate over your class in a for..of
loop.
But yes, here the user is still responsible with calling initialize
.
The suggested static constructor syntax remains to be superior IMO and has best of everything: :relieved:
Closing as Too Complex - we would need a more compelling use case since module
merging provides a very close approximation to the desired behavior.
Anders brought up that we also need to be conservative here so that we don't misalign with the ES6 class proposal (which also lacks static constructors).
we don't misalign with the ES6 class proposal (which also lacks static constructors).
:+1:
Agreed. Let's keep the pace with ES6.
Though, I think I elaborated a little too much on this feature. I realized, specifically, moving the static constructor block to the end of the class was not necessary to defer its invocation. It is deferred anyway because it is in another function that is invoked when it is completely loaded. To test that:
var MyClass = (function () {
function MyClass() {
}
(function () {
alert("static ctor invoked");
})();
return MyClass;
});// do not invoke yet
alert("class loaded");
// invoke
MyClass();
In case anyone is reaching this issue, you can also do it this way:
class MyClass {
private static _constructor = (() => {
console.log('Static constructor');
})();
}
This has the advantage of being a concise syntax not exposing any unnecessary variable.
@aymericbeaumet That is my preferred solution so far and is also noted in the first post.
Didn't catch you proposed the same solution!
:+1: Please do! However, a potential workaround would be:
class MyClass {
private static isInitialized: boolean = false;
public static init() {
if (!MyClass.isInitialized) {
// Initialize things...
}
MyClass.isInitialized = true;
}
}
MyClass.init();
Developers of TS, please, read about D language, where most problems with OOP are solved. For inspiration.
Why i can not use "this" in "static" context? IMHO, "this" must point to current class (not to superclass).
@nin-jin You can use this
in static
member functions and accessors, just not member initializers.
Note that in any comparison to other languages you need to consider the somewhat unique constraints TypeScript operates with (as a superset of JavaScript and emitting clean, idiomatic JavaScript with minimal or no strange rewriting). Were we designing in a green field with only our own thoughts the language would look very different :)
But what about inheritance?
class A {
private options: {hello: number}
static constructor() {
console.log('called')
this.options = {hello: 123}
}
}
class B extends A {
}
I would expect _called_ to be logged twice.
@felixfbecker Every static property is bind to the class. It cannot be inherited. So the static constructor should also follow this pattern to ensure a equal behavior.
@PascalSenn static properties _are_ inherited in ES6 and TypeScript.
@felixfbecker Thats new for me. Didnt know that. C# doesnt support it, so i thought Typescript neither.
@PascalSenn you can even do it in ES5:
function SuperClass {}
SuperClass.staticProperty = 123;
function SubClass {}
Object.setPrototypeOf(SubClass, SuperClass);
@RyanCavanaugh I don't think the merged module solution is anywhere near an approximation to a static constructor. It is still required that you call the static member whether you use merged modules or not. A static constructor would initialise automatically.
It's too bad generic type references were dropped in static functions. I was using some static constructor style methods on some classes (similar to JoshMcCullough's post above) and now it's all broken. The only workaround was to convert the private static constructor methods to private instance methods and call them instead via the prototype from another static constructor method (or directly after the class). I didn't want to duplicate my class generic signature on my static method as well, that's just getting too silly.
Hello,
I hope Typescript will implement static constructors like this:
https://github.com/sirisian/ecmascript-static-constructor/issues/1
It would solve "function properties" typing problem too: https://stackoverflow.com/questions/12766528/build-a-function-object-with-properties-in-typescript
@Alphapage TypeScript supports many coming ES6+ changes today, best it can, and targeting such code will output code like this class MyClass { }
- which works in non-IE browsers. ;) However, what you are suggesting - to support an invokable 'MyClass()' constructor - is an error since, currently, ES6 introduces a new non-invokable function type (for classes). The minute someone targets ES6+, their code would fail. ;) The spec would need to be changed regarding non-callable class functions in upcoming approval stages at some ECMAScript proposal level before this was even considered.
BTW: There's already a proposal here as well: https://github.com/tc39/ecma262/blob/master/workingdocs/callconstructor.md
I'm not sure of the exact mechanics of some of the tree shakers out there, but won't some of these strategies disrupt the tree shakers? Calling a static method on the Type as a separate action outside of the class's IIFE (in ES5) may be seen as a reference to the type? Or containing some side effect that may not be possible to omit? Presumably being able to wrap some code inside of the class's ES5 iffe would been seen as more atomic with the definition of the type?
@gmurray81 tree shaking is partial workaround of autoloaders absence. Better to use bundler that supports autoloading. MAM in example.
@nin-jin even if you could just persuade everyone to use different technology in their projects to avoid a problem (some of us need to write library code for others to consume) there would still be an argument that it should be easy to make the creation/calling of the static cons atomic with the class creation.
It seems like shoving an IIFE on the last defined static property of a type, as suggested above, might be the closest you can come with the syntax as it stands. It's a little ugly, though, comparatively.
@aymericbeaumet The solution you provided may caused inspector report a 'unused variable' warning which cannot be silenced in webstorm... (I cannot write better one until now. Thinking.)
Most helpful comment
In case anyone is reaching this issue, you can also do it this way:
This has the advantage of being a concise syntax not exposing any unnecessary variable.